POST request with parameters doesn't work with koa-router - http-post

I'm trying to build a simple REST API with Koa. For this, I am using koa-router. I have two problems:
Whenever I try to add parameters to my POST-Method in mainRouter.ts like ":id", Postman shows a "not found". My request: http://localhost:3000/posttest?id=200
I cannot get the parameters with "ctx.params". I also can't find anything about it on the koajs-page, but I do see examples like this everywhere?!
This is my app:
app.ts
import * as Koa from 'koa';
import * as mainRouter from './routing/mainRouter';
const app: Koa = new Koa();
app
.use(mainRouter.routes())
.use(mainRouter.allowedMethods());
app.listen(3000);
mainRouter.ts
import * as Router from 'koa-router';
const router: Router = new Router();
router
.get('/', async (ctx, next) => {
ctx.body = 'hello world';
});
router
.post('/posttest/:id', async (ctx, next) => {
ctx.body = ctx.params.id;
});
export = router;
If I change the POST-method to this, then I get "200":
router
.post('/posttest', async (ctx, next) => {
ctx.body = ctx.query.id;
});

If you're using a query string in your request like this:
http://localhost:3000/posttest?id=200
Then your route handler should be using ctx.query, not ctx.params:
router.post('/posttest', async (ctx, next) => {
console.log(ctx.query.id); // 200
});
You should only use ctx.params when you want to send requests like this:
http://localhost:3000/posttest/200
In which case you would write the route handler like so:
router.post('/posttest/:id', async (ctx, next) => {
console.log(ctx.params.id); // 200
});

Related

remix fetch data within a loader from another loader

Trying to fetch data internally within remix application
from an api route located in
/routes/api/test.tsx
export const loader = async ({ request }: LoaderArgs) => {
const testRes = await fetch("/api/test");
return await testRes.json();
};
I get error Invalid URL
what is the right convention for this?
If you want to call another URL you have to pass in the full url. Something like:
export const loader = async ({ request }: LoaderArgs) => {
const testRes = await fetch("https://example.com/api/test");
return await testRes.json();
};
To have the domain be dynamically set you could use the URL from the request. Something like this:
export const loader = async ({ request }: LoaderArgs) => {
const domain = new URL(request.url).hostname;
const testRes = await fetch(`${domain}/api/test`);
return await testRes.json();
};

How to test dispatched react function using Jest

I am trying to unit test a function which makes an async call using an Axios helper instance. I have attempted multiple ways of trying to unit test this but I can not seem to find any material online which has helped. I've been stuck on this problem for a few days which is frustrating so any help would be appreciated! Below are the Axios Helper file (api.js)
api.js
import axios from 'axios'
const API = (token = null) => {
let headers = {
'Content-Type': 'application/json',
'Ocp-Apim-Subscription-key': process.env.NEXT_PUBLIC_API_HEADER_SUBSCRIPTION_KEY
}
if (token) {
const tokenHeader = { Authorization: 'Bearer ' + token }
headers = { ...headers, ...tokenHeader }
}
const url = process.env.NEXT_PUBLIC_API_BASE_URL
const API = axios.create({
baseURL: url,
headers
})
return API
}
export default API
mocked API
export default {
post: jest.fn(() =>
Promise.resolve({
data: {}
})
),
get: jest.fn(() =>
Promise.resolve({
data: {}
})
)
}
action file
export const initiate2FA = (destinationValue) => async () => {
const twoFactorAuth = destinationValue
const res = await API().post('/foo', {
Destination: twoFactorAuth
})
return res
}
Action.test.js
import API from 'api/api'
import { initiate2FA } from 'actions/userActions'
jest.mock('api/api')
const mockedAxios = API
const dispatch = jest.fn()
describe('Initiate2FA function', () => {
it('bar', async () => {
mockedAxios.get.mockImplementationOnce(() => Promise.resolve({ status: 200 }))
const t = await dispatch(initiate2FA('test#test.com'))
console.log(t)
})
})
My issue with the above test file is that it returns an anonymous function and I do not know how to handle this to pass the unit test. The goal of the test is to make sure the function is called. I am not sure if I am approaching this the correct way or should change my approach.
Again, any suggestions would be great!
Mocking an API call is something you can mock on your own React component, instead of a function, and the best option would be to not mock anything on your component. Here you can read all about why you should not mock your API functions. At the end of the article, you're going to find a library called Mock Service Worker which you can use for your purpose.
The way you declare you have an actual HTTP called that needs to be mocked would be something like this:
rest.get('/foo', async (req, res, ctx) => {
const mockedResponse = {bar: ''};
return res(ctx.json(mockedResponse))
}),
If you just need to unit test a function, you can still use Mock Service Worker to resolve the HTTP request, and then test what happens after that. This would still be your first choice. And the test would look like:
// this could be in another file or on top of your tests.
rest.get('/foo', async (req, res, ctx) => {
const mockedResponse = {bar: ''};
return res(ctx.json(mockedResponse))
}),
// and this would be your test
describe('Initiate2FA function', () => {
it('bar', async () => {
const res = await initiate2FA('test#test.com');
expect(res).toBe({bar: '');
})
})

How to handle apollo errors in one place with react-hooks?

I'm using React with hooks + GraphQL.
My app.jsx:
import { onError } from 'apollo-link-error';
...
const httpLink = ...
const errorLink = onError(err => console.log(err))
const terminatingLink = split(...httpLink, errorLink)
const client = new ApolloClient({
link: ApolloLink.from([terminatingLink])
...
})
<ApolloProvider client={client}>
<ErrorProvider>
</ErrorProvider>
</ApolloProvider>
Error provider is used as a common state for errors, i.e. if mutation response is bad.
Currently I create onError handler for each(!) mutation and query, i.e. like this:
const [createTeam] = useMutation(createTeamQ, {
onError: (err) => { dispatchError(err) }
})
This looks like overcoding, since I do it too often.
But I can't figure out how to dispatch the error in AppolloClient only once in onError() function imported from 'apollo-link-error'. I can't use hooks there.
Should I use redux for this particular case?
Thanks in advance.

How to define context object for graphql subscription server

With the normal graphql server we can define the context object like this:
app.use('/graphql', graphqlExpress(async (req) => {
return {
schema,
context: {
app,
params,
}
};
}));
** subscription server **
How can I do the same for the subscription server? (Doing the hybrid http / websocket approach). Can't seem to find a solution from the docs.
new SubscriptionServer({
execute,
subscribe,
schema,
onConnect: (connectionParams, webSocket) => {
console.log(connectionParams);
}
}, {
server,
path: '/subscriptions'
});
You can add a middleware before the execute function and add the required context before resolving the subscription.
It could look like this:
const middleware = (args) => new Promise((resolve, reject) => {
const [schema, document, root, context, variables, operation] = args;
context.app = <your app parameter>;
context.params = <your params>;
resolve(args);
});
SubscriptionServer.create({
schema,
subscribe,
execute: (...args) => middleware(args).then(args => { return execute(...args); }) },
{
server: httpServer,
path: "/subscription",
},
);
As you can see you have all the data from the request in the args of the execute function.

REST API with koa2. Common prefix for several routers

I have two entities, users and employees. So I want CRUD for both in different endpoints, but both of them will be mounted under "api", so I can define api_v1, api_v2 and so on.
The endpoints would be something like:
get api/users
put api/users/12
delete api/users/12
get api/employees
....
I can't get "api" prefix for both of my routes. Can't get it working with koa-mount.
My files:
server.js
// Dependencies
import Koa from 'koa'
import mongoose from 'mongoose'
import logger from 'koa-logger'
// import parser from 'koa-bodyparser';
import convert from 'koa-convert'
import serve from 'koa-static'
import Router from 'koa-router'
import session from 'koa-generic-session'
import mount from 'koa-mount'
// A seperate file with my routes.
import routingUsers from './users'
import routingEmployees from './employees'
// config
const config = require("./config/config")
// connect to the database
mongoose.connect(config.mongo.url)
mongoose.connection.on('error', console.error)
// Creates the application.
const app = new Koa()
// how to use koa-mount to make this work? Arghhhhh!
// const api = new Koa();
// api.use(convert(mount ('/api', app)))
// trust proxy
app.proxy = true
// sessions
app.keys = ['your-session-secret']
// Applies all routes to the router.
const user = routingUsers(Router())
const employee = routingEmployees(Router())
app
.use(logger()) // log requests, should be at the beginning
.use(user.routes()) // asign routes
.use(employee.routes()) // asign routes
.use(user.allowedMethods())
.use(employee.allowedMethods())
.use(convert(session())) // session not needed for an API??????
.use(convert(serve(__dirname + '/public'))) // for static files like images
// Start the application.
app.listen(3000, () => console.log('server started 3000'))
export default app
users.js (employees.js is the same).
// Export a function that takes the router
export default router => {
// Set a prefix of our api, in this case locations
const api = 'users'
router.prefix(`/${api}`);
// GET to all locations.
router.get('/', (ctx, next) =>
ctx.body = 'hello users');
// ctx.body = await Location.find());
// POST a new location.
router.post('/', async (ctx, next) =>
ctx.body = await new Location(ctx.request.body).save());
// Routes to /locations/id.
router.get('/:id', async (ctx, next) =>
ctx.body = await Location.findById(ctx.params.id));
// PUT to a single location.
router.put('/:id', async (ctx, next) =>
ctx.body = await Location.findByIdAndUpdate(ctx.params.id, ctx.body));
// DELETE to a single location.
router.delete('/:id', async (ctx, next) =>
ctx.body = await Location.findByIdAndRemove(ctx.params.id));
return router;
}
I use the following solution:
import Router from 'koa-router';
const router = new Router()
.get('/', function(ctx) {
ctx.body = 'Index';
});
const apiRouter = new Router({
prefix: '/api'
})
.get('/templates', Templates.index)
.post('/templates', Templates.store)
.put('/templates', Templates.update)
.get('/lists', Lists.index);
router.use(apiRouter.routes());
Finally I've sent another parameter to the routers modules, so I used router prefix:
// Applies all routes to the router.
const user = routingUsers(Router(), 'api/users/')
const employee = routingEmployees(Router(), 'api/employees/')
Users would be:
export default (router, prefix) => {
// Set a prefix of our api, in this case locations
// const api = 'users'
router.prefix(`/${prefix}`);
....

Resources