REST API with koa2. Common prefix for several routers - koa

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}`);
....

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 access privateRuntimeConfig declared in Nuxt.config.js in serverMiddleware?

How to access configuration declared in privateRuntimeConfig in Nuxt.config.js file in serverMiddleware?
$config and context are not available in serverMiddleware.
I am using serverMiddleware in Nuxtjs to write api.
Its getting called however I am trying to pass some configuration from privateRuntimeConfig in Nuxt.config.js file.
const bodyParser = require('body-parser')
const app = require('express')()
const { uuid } = require('vue-uuid')
const productsModule = require('../lib/bal/products')
app.use(bodyParser.json())
app.post('/create', (req, res) => {
console.log('Config:' + String(req))
const result = productsModule.createProduct(this.$config, req.body.name, 'Trial product', '', 10, false, uuid.v1)
if (result === undefined) {
res.status(500).json({ error: 'Failed to create product. Try again!' })
return
}
console.log(result)
res.status(200).json(result)
})
module.exports = app
Yes you are right, since serverMiddleware only runs at service-side you can't use this.$config or context.$config.
What i did is, if it is a static data, i use environment variables to call the data.
.env file
APP_USERNAME=M457ERCH1EF
serverMiddleware file i.e xxx.js
....
const username = process.env.APP_USERNAME
....

Why am I getting 'Application Error' when deploying to Heroku? (MERN app)

I've checked the logs and this is what it says:
Here is my app.js file (at the root level):
// Importing frameworks, libraries, routes, and JSON parser
const express = require('express');
const mongoose = require('mongoose');
const rewards = require('./routes/api/rewards');
const bodyParser = require('body-parser');
const path = require('path');
// Loading static build folder for production
if (process.env.NODE_ENV === 'production') {
app.use(express.static('frontend/build'));
app.get('/', (req, res) => {
res.sendFile(path.resolve(__dirname, 'frontend', 'build', 'index.html'));
})
}
// Create a new Express server
const app = express();
// Setup middleware for body parser
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// Import MongoDB key
const db = require('./config/keys').mongoURI;
// Connect to MongoDB using Mongoose
mongoose
.connect(db, {useNewUrlParser: true, useUnifiedTopology: true})
.then(() => console.log('Connected to MongoDB successfully'))
.catch(err => console.log(err));
// Create basic express routing
app.use('/api/rewards', rewards);
// Determining which port to run on
// Deploying on Heroku requires the server to be on process.env.PORT
const port = process.env.PORT || 5000;
// Start a socket and listen for connections on the path
// Display a success message when server is running correctly
app.listen(port, () => console.log(`Server is running on port ${port}`));
Any pointers/tips on what this error message is saying exactly? My best guess is that it has something to with one of my routes but I'm not too sure...
You are making your app variable below your if of production check to serve react build.
please move line
const app = express();
above your if check.
correct app.js would be:
// Importing frameworks, libraries, routes, and JSON parser
const express = require('express');
const mongoose = require('mongoose');
const rewards = require('./routes/api/rewards');
const bodyParser = require('body-parser');
const path = require('path');
// Create a new Express server
const app = express();
// Loading static build folder for production
if (process.env.NODE_ENV === 'production') {
app.use(express.static('frontend/build'));
app.get('/', (req, res) => {
res.sendFile(path.resolve(__dirname, 'frontend', 'build', 'index.html'));
})
}
// Setup middleware for body parser
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// Import MongoDB key
const db = require('./config/keys').mongoURI;
// Connect to MongoDB using Mongoose
mongoose
.connect(db, {useNewUrlParser: true, useUnifiedTopology: true})
.then(() => console.log('Connected to MongoDB successfully'))
.catch(err => console.log(err));
// Create basic express routing
app.use('/api/rewards', rewards);
// Determining which port to run on
// Deploying on Heroku requires the server to be on process.env.PORT
const port = process.env.PORT || 5000;
// Start a socket and listen for connections on the path
// Display a success message when server is running correctly
app.listen(port, () => console.log(`Server is running on port ${port}`));

Service worker not creating networkFirst Cache

My Service Worker:
importScripts('https://storage.googleapis.com/workbox-
cdn/releases/3.0.0/workbox-sw.js');
//Use Workbox Precache for our static Assets
workbox.precaching.precacheAndRoute([]);
console.log('this is my custom service worker');
//Create articles Cache from online resources
const onlineResources = workbox.strategies.networkFirst({
cacheName: 'articles-cache',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 50,
}),
],
});
workbox.routing.registerRoute('https://newsapi.org/(.*)', args => {
return onlineResources.handle(args);
});
The precache cache works but the onlineResources Cache is never created.
A look at my file structure:
So I don't think scope is an issue even though I cant see clients in my service worker on Chrome dev tools.
Lastly here is my app.js file:
//main populates main tags in indexpage
const main = document.querySelector('main');
//this populates the source dropdown menu with sources
const sourceSelector = document.querySelector('#sourceSelector');
//set default source so page loads this
const defaultSource = 'bbc-news';
//on window load call update news and when update
window.addEventListener('load', async e => {
updateNews();
await updateSources();
sourceSelector.value = defaultSource;
//when sourceSelector is changed update the news with the new source
sourceSelector.addEventListener('change',e =>{
updateNews(e.target.value);
});
//checks for serviceWorker in browser
if('serviceWorker'in navigator){
try{
//if there is register it from a path
navigator.serviceWorker.register('/sw.js');
console.log('registered!');
} catch(error){
console.log('no register!',error);
}
}
});
async function updateNews(source= defaultSource){
//response awaits a fetch of the news API call
const res = await fetch(`https://newsapi.org/v2/top-headlines?sources=${source}&apiKey=82b0c1e5744542bdb8c02b61d6499d8f`);
const json = await res.json();
//fill the html with the retrieved json articles
main.innerHTML = json.articles.map(createArticle).join('\n');
}
//Update the news source
async function updateSources(){
const res = await fetch(`https://newsapi.org/v2/sources?apiKey=82b0c1e5744542bdb8c02b61d6499d8f`);
const json = await res.json();
//Whatever source the sourceSelector picks gets mapped and we take the id of it - It is used with updateNews();
sourceSelector.innerHTML = json.sources
.map(src=>`<option value="${src.id}">${src.name}</option>`).join('\n');
}
function createArticle(article){
return ` <div class="article">
<a href="${article.url}">
<h2>${article.title}</h2>
<img src="${article.urlToImage}">
<p>${article.description}</p>
</a>
</div>
`;
}
App.js plugs into newsAPI and outputs the JSON to the pages HTML.
When you register the route you seem to be trying to use a regex as a string. I think it is literally interpreting the route as a string that includes .*. Instead try the regex /^https:\/\/newsapi\.org/ which per the docs will match from the beginning of the url.

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

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
});

Resources