so far I have been able to establish Test email delivery on strapi but I am not having any luck with posting a request when I am using the postman. For example I created route
// api/email/config/routes.json
{
"routes": [
{
"method": "POST",
"path": "/emails",
"handler": "Email.send",
"config": {
"policies": []
}
}
]
}
and then rest of the email body here is an example:
// File /api/email/controllers/Email.js
'use strict'
/**
* Read the documentation () to implement custom controller functions
*/
module.exports = {
/**
* Sends an email to the recipient in the body of the request
*/
send: async (ctx) => {
const body = ctx.request.body
const sendTo = body.email
strapi.log.debug(`Trying to send an email to ${sendTo}`)
try {
const emailOptions = {
to: sendTo,
subject: 'This is a test',
html: `<h1>Welcome!</h1><p>This is a test HTML email.</p>`,
}
await strapi.plugins['email'].services.email.send(emailOptions)
strapi.log.debug(`Email sent to ${sendTo}`)
ctx.send({ message: 'Email sent' })
} catch (err) {
strapi.log.error(`Error sending email to ${sendTo}`, err)
ctx.send({ error: 'Error sending email' })
console.log('====================================');
console.log(err);
console.log('====================================');
}
},
}
after running the application I posted request http://localhost:1337/emails and tried sending this json body {"email": "validemail#gmail.com"} but I keep receiving Method Not Allowed I can't seem to find what did I do wrong. Thank you in advance!
Related
I'm following the Messagebird docs on sending an SMS through the messagebird API. When I run my app and go to the link in postman, I get no errors and "SUCCESS" is console.logged as well as the reponse. However I never recieve a text. When I go to the SMS logs on the messagebird dashboard there's nothing there except for the test SMS I sent rhough the messagebird dashboard
I've replaced my number for privacy purposes but there was no issue regarding the number being invalid
router.get("/testSMS", (req,res) => {
messagebird.messages.create({
originator : 'Movie App',
recipients : [ '123456778' ],
body : 'Hello World, I am a text message and I was hatched by Javascript code!'
}, function (err, response) {
if (err) {
console.log("ERROR:");
console.log(err);
} else {
console.log("SUCCESS:");
console.log(response);
}
});
})
Here's my console
This example works for me. If you add your number as a query param does this work for you?
router.get("/test/:phone", (req, res) => {
const { phone } = req.params;
// Ensure the phone nubmer follows the E.164 format (https://www.twilio.com/docs/glossary/what-e164)
if (!/^\+[1-9]{1}[0-9]{3,14}$/.test(phone)) {
return res.status(400).send("Invalid phone number");
}
// Sends a test SMS to the number specified in the request
messagebird.messages.create(
{
originator: "MessageBird",
recipients: [phone],
body: "This is a test message from MessageBird",
},
(err, response) => {
if (err) {
return res.send(err);
}
return res.send(response);
}
);
});
When i try to login using Talend API Tester, getting this into terminal:
API resolved without sending a response for /api/auth/callback/credentials, this may result in stalled requests.
Besides, here is the image of Request:
I followed this: Next-Auth.js -> Rest API
Why I'm getting this kind of response & warning?
Below is the [..nextauth.js] file's code. What's wrong with my code?
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
import prisma from '../../../lib/prisma'
const options = {
providers: [
Providers.Credentials({
name: 'Credentials',
credentials: {
email: { label: "Email", type: "email", placeholder: "something#example.com" },
password: { label: "Password", type: "password" }
},
async authorize(credentials) {
const {email, password} = credentials
const user = await prisma.user.findFirst({ where: { email, password } })
console.log(user);
// If no error and we have user data, return it
if (user) {
return user
}
// Return null if user data could not be retrieved
return null
}
})
]
}
export default async function handle(req, res) {
console.log('I see');
NextAuth(req, res, options)
}
Note: Actually I don't want to send csrfToken in the body. Only email & password to the endpoint. Please have a look into my this question How to do authentication using NextAuth.js without login page (using postman)
hoping you all can help me solve this mystery. I’ve created an endpoint for my API:
apiRouter.get("/username", async (req, res) => {
const { username } = req.body;
try {
const user = await getUserByUsername(username);
res.send({ message: "This is your user", user });
console.log("this is user", user);
} catch (error) {
throw error;
}
});
It’s a GET route that should send back the a message and a user object in this fashion when the user interacts with this url path:
http://localhost:3000/api/users/username
When I access the endpoint via, Postman, I get back both the message and the user object. Something that looks like this:
{
"message": "This is your user",
"user": {
"customer_id": 5,
"first_name": "Jessica",
"last_name": "Blue",
"email": "baker#space.com",
"username": "jess1",
"password": "1234",
"address_1": "123 pretty lane",
"address_2": "Apt 31",
"city": "Riverside",
"state": "California"
}
}
However, when I access the route, via my frontend, the message comes back but not the user. I’m using the following method to make the call to the server:
export async function getUserByUsername(username) {
try {
const { data } = await axios.get("/api/users/username", {
username,
});
console.log(data);
return data;
} catch (error) {
throw error;
}
}
I'm really not sure why the user isn’t being sent back with the response. The route works, I’m just not getting back my expected response. I’ve spent a couple hours trying to find a fix with no luck. Any help would be greatly appreciated.enter image description here
Currently you can't send a body with get requests in most browsers. so you've two options:
change your backend method to POST:
apiRouter.post("/username", async (req, res) => {
const { username } = req.body;
.
.
.
}
and use axios like this:
const { data } = await axios.post("/api/users/username", {
username,
});
or if you want to stick with the get method accept the username as a query or a param:
apiRouter.get("/username/:username", async (req, res) => {
const { username } = req.params;
.
.
.
}
and use axios like this:
const { data } = await axios.get(`/api/users/username/${username}`);
or
apiRouter.get("/username", async (req, res) => {
const { username } = req.query;
.
.
.
}
and use axios like this:
const { data } = await axios.get(`/api/users/username`, { params: { username } });
You are using a GET request with body. Although it's possible, but not advisable, to do so when you create your own request through Postman, axios probably ignores the body param when using a GET request and doesn't include it in the request.
So my suggestion would be to place the username param in the request params (req.params) or request query (req.query) and try to get it from there.
You can find more details about req.params: here, and about req.query: here.
I think it's might be because of the async await approach you're using in the endPoint it self, can you try running this line in your terminal:
npm install express-async-errors --save
after that go to index.js (your backend route file) and add this line over here:
import 'express-async-errors';
and try again, Hopefully, this will solve your issue
I was starting with GraphQL and I was unable to comprehend how we can throw errors in GraphQL
I went through a couple of articles on the web but almost all of them use Apollo and the code-structure looks very different than how I work.
Consider this piece of code, here where I am making a mutation, now how can send a response message with error and change headers status message in case of error?
AddNewPersonalInfo: {
type: userDashboardType,
args: {
parameter: {
type: userCreationlInputType
}
},
resolve: async (parent, args, context) => {
args.parameter.userId = context.req.headers.userId
//Check if user info already exsist
const checkIfUserInformationExsist = await getSelectedThingFromTable('CatsWork_personal', 'userId', `${userId}`)
if (checkIfUserInformationExsist[0]) {
const error = {
code: 403,
message: 'User info Already exsist'
}
throw new Error(error)
} else {
try {
const addLinkedinUser = await insertIntheTable('personal', payload)
return true
} catch (err) {
console.error(err)
throw new Error(err)
}
}
}
}
What I have faced in one of my projects, it is hard to set the status code of the response. So, I made some custom error response to identify correct statusCode using express-graphql
Below is the example (What I have used in one of my projects):
--------app.js file--------
const graphqlHTTP = require('express-graphql')
app.use('/graphql', (req, res) => {
graphqlHTTP({
schema: GraphQLSchema, //A GraphQLSchema instance from GraphQL.js. A schema must be provided.
graphiql: true,
context: { req },
formatError: (err) => {
const error = getErrorCode(err.message)
return ({ message: error.message, statusCode: error.statusCode })
}
})(req, res)
})
--------getErrorCode function implementation--------
const { errorType } = require('../constants')
const getErrorCode = errorName => {
return errorType[errorName]
}
module.exports = getErrorCode
--------Constant.js file--------
exports.errorName = {
USER_ALREADY_EXISTS: 'USER_ALREADY_EXISTS',
SERVER_ERROR: 'SERVER_ERROR'
}
exports.errorType = {
USER_ALREADY_EXISTS: {
message: 'User is already exists.',
statusCode: 403
},
SERVER_ERROR: {
message: 'Server error.',
statusCode: 500
}
}
Now, we are ready to use our setup.
From your query or mutation, you need to require constant file and return custom error:
const { errorName } = require('../constant')
AddNewPersonalInfo: {
type: userDashboardType,
args: {
parameter: {
type: userCreationlInputType
}
},
resolve: async (parent, args, context) => {
args.parameter.userId = context.req.headers.userId
//Check if user info already exsist
const checkIfUserInformationExsist = await getSelectedThingFromTable('CatsWork_personal', 'userId', `${userId}`)
if (checkIfUserInformationExsist[0]) {
const error = {
code: 403,
message: 'User info Already exsist'
}
throw new Error(errorName.USER_ALREADY_EXISTS) // Here you can use error from constatnt file
} else {
try {
const addLinkedinUser = await insertIntheTable('personal', payload)
return true
} catch (err) {
console.error(err)
throw new Error(errorName.SERVER_ERROR) // Here you can use error from constatnt file
}
}
}
}
--------Error response--------
{
error: [{
"statusCode": 403,
"message": "User is already exists."
}],
data: null
}
We just need to write custom error handling from FS side too.
Note:- formatError: is deprecated and replaced by customFormatErrorFn. It will be removed in version 1.0.0. You can refer customFormatErrorFn.
graphql should be an application level layer that shouldn't (see last paragraph why shouldn't and not doesn't) require http to work. Although in 99% of cases it runs on top of http, because of how convenient it is to do so, graphql is itself a layer 7 protocol.
What does that mean in your case? Well, it means you should not mix concepts from HTTP/REST with concepts from graphql and focus on the latter. The headers error code is a HTTP/REST concept, graphql sends errors in the errors field of the response and the nodejs implementation already catches all your errors and adds them to the list. The HTTP status will be always 200, and your clients shouldn't care and consume your graphql api and not a mix of REST with graphql.
That being said, there are couple of things that REST over HTTP does better. So people, including the developers of Apollo, kinda mixed concepts too, mainly because the graphql standard is not complete (aka, it doesn't have a standard/rule for solving all the problems you might encounter while building an API), so people improvised. I wouldn't recommend graphql yet for any serious project.
Reference
You can specify an error function inside graphqlHTTP like this:
app.use("/graphql", graphqlHTTP({
schema,
graphiql: true,
customFormatErrorFn: err => {
try {
err.details = JSON.parse(err.message);
err.message = Array.isArray(err.details.error) ? err.details.error.join(",") : err.details.error;
return err;
} catch {
return err;
}
}
}));
where err.message might contain a JSON object or a string.
you can use those function to generate specific client and server error functions:
const clientError = error => new Error(JSON.stringify({
success: false,
code: 400,
error
}));
const serverError = ({ name, message, stack }) => new Error(JSON.stringify({
success: false,
error: "Server Error",
code: 500,
name,
message,
stack
}));
const userValidationError = err => {
if (err.name === "ValidationError") return clientError(Object.values(err.errors).map(({ message }) => message));
return serverError(err);
}
module.exports = {
clientError,
serverError,
userValidationError
};
userValidationError function is useful if you have a mongodb validation error.
so that you would use it inside resolve function like this:
try {
const createdDocument = await MongooseDoc.create(data);
return createdDocument;
} catch (err) {
throw userValidationError(err);
}
the response would be
{
"errors": [
{
"message": "error details 1,error details 2",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"document"
],
"details": {
"success": false,
"code": 400,
"error": [
"error details 1",
"error details 2"
]
}
}
],
"data": {
"document": null
}
}
if you want to throw a clientError you throw it outside try catch.
Hopefully this code helps someone send dynamic error messages in graphql.
Before the server starts, all plugins etc are registered. I create my strategy and set JWT as default authentication method for the server.
await server.register(require('hapi-auth-jwt2'));
await server.register(require('~/utils/jwt-key-signer'));
server.auth.strategy(
'jwt', 'jwt',
{ key: process.env.API_KEY,
validate: true, // Temporarily using true
verifyOptions: { algorithms: [ 'HS256' ] }
});
server.auth.default('jwt');
Here's my route. I pass the payload from the handler request into a plugin that signs my key and returns a token:
'use strict';
const Boom = require('boom');
exports.plugin = {
name: 'user-create',
register: (server, options) => {
server.route({
method: 'POST',
path: '/user/create',
options: { auth: 'jwt' },
handler: async (request, h) => {
const { payload } = await request;
const { JWTKeySigner } = await server.plugins;
const token = await JWTKeySigner.signKeyReturnToken(payload);
const cookie_options = {
ttl: 365 * 24 * 60 * 60 * 1000, // expires a year from today
encoding: 'none', // we already used JWT to encode
isSecure: true, // warm & fuzzy feelings
isHttpOnly: true, // prevent client alteration
clearInvalid: false, // remove invalid cookies
strictHeader: true // don't allow violations of RFC 6265
}
return h.response({text: 'You have been authenticated!'}).header("Authorization", token).state("token", token, cookie_options);
},
options: {
description: 'Creates a user with an email and password',
notes: 'Returns created user',
tags: ['api']
}
});
}
};
Here's how I sign my key:
const jwt = require('jsonwebtoken');
exports.plugin = {
name: 'JWTKeySigner',
register: (server, options) => {
server.expose('signKeyReturnToken', async (payload) => {
jwt.sign(payload, process.env.API_KEY, { algorithm: 'HS256' }, async (err, token) => {
if (err) {
console.log(err)
}
await console.log(`TOKEN${token}`);
return token;
});
})
}
};
I then visit my route from Postman, then pass my user which contains an email address and password back to the round as JSON, and this is the response I get:
{
"statusCode": 401,
"error": "Unauthorized",
"message": "Missing authentication"
}
Ok, so that proves my route is successfully being protected. I now proceed to add my token into Postman:
Then I get this error:
{
"statusCode": 500,
"error": "Internal Server Error",
"message": "An internal server error occurred"
}
If I remove the token from postman, I get the "unauthorised" error.
All I'm trying to do is block outside access to my API, and only allow people who have permission to access it. This would be regular users who signup.
When I paste my token into JWT.io, I can see my data on the right hand side of the page, but JWT tells me it's an invalid signature.
I'd really appreciate some clarity here. I'm using hapi-auth-jwt2.
Thanks in advance
Hmm, I was writing another message to you but then I checked hapi-auth-jwt2 docs for validate options and it says;
validate - (required) the function which is run once the Token has been decoded with signature
async function(decoded, request, h) where:
decoded - (required) is the decoded and verified JWT received in the request
request - (required) is the original request received from the client
h - (required) the response toolkit.
Returns an object { isValid, credentials, response } where:
isValid - true if the JWT was valid, otherwise false.
credentials - (optional) alternative credentials to be set instead of decoded.
response - (optional) If provided will be used immediately as a takeover response.
Just try
server.auth.strategy(
'jwt', 'jwt',
{ key: process.env.API_KEY,
validate: validate: () => ({isValid: true}), // Temporarily using true
verifyOptions: { algorithms: [ 'HS256' ] }
});
Then let's see if 500 error continues.
Maybe some other thing in your code that throws an error. Did you enable debug on your server setup? You should see the details of that 500 error in your server console.