How to use Browser history in react router with koa - javascript

In the express, we can just use following codes to deal with the request. The server side will send index.html when the request that isn't handled by router.
app.get('*', function (request, response){
response.sendFile(path.resolve(__dirname, '../public', 'index.html'))
})
But in koa, the following code don't work. When the request isn't handled by koa-router, it will return 404 instead of index.html.
var send = require('koa-send')
var serve = require('koa-static')
var router = require('koa-router')
var koa = require('koa')
var app = koa();
app.use(serve(__dirname+'/../public'));
app.use(function *(){
yield send(this, path.join(__dirname, '/../public/','index.html' )); })
app.use(router.routes())
following code also don't work
router
.get('*', function* () {
yield send(this, __dirname +'/../public/index.html')
})

router.get('*', async function(ctx, next) {
var html = fs.readFileSync(path.resolve('./build/index.html'));
ctx.type = 'html';
ctx.body = html;
})
this works for me

Essentially what you're trying to achieve is server-rendering.
You need to write route configuration with match & RouterContext. react-router has detailed documentation for this.
Server Rendering in react-router
In case of koa, it can roughly be done in this way.
import router from 'koa-router'
import { match, RouterContext } from 'react-router'
const koaRouter = router()
const otherRouter = () => {
return new Promise((resolve, reject) => {
match({ routes, location }, (error, redirectLocation, renderProps) => {
...
..
.
}
}
koaRouter
.use(otherRouter)
I found couple of repos online which seem pretty decent. I haven't verified them though.
breko-hub
koa-react-isomoprhic

Related

Method of Router class is not a function

I am writing a middleware to check for authentication of a user, and want to shorten the amount of repetetive code by writing one function for the entire Router class.
Here's how I do it:
const express = require('express')
express.Router.checkAuthorization = (address) => {
return function(req, res, next) {
if(req.session.user)
next()
else
Router.redirect(res, address, 401)
}
}
What's more interesting, is that I route Router.redirect using the same principle, and it works just fine:
express.Router.redirect = (res, path, status) => {
res.status(status || 308)
res.redirect(path)
}
Albeit, when using it in an instance of a router, it says that checkAuthorization is not a function, even though when I console.log it, it says that it is an anonymous function.
Here's how I do it:
const { Router } = require('express')
router.get('/profile', Router.checkAuthorization('/authorize'),
controller.displayUserProfile)
Why doesn't it recognize it as a function?

Error On Express Router Setup: Router.use() requires middleware function but got a undefined

I will just say first of all that I am aware of almost all the questions asked on this site under this title.
The solutions there were pretty obvious and already done by me (with no success) or only helped for those specific cases and didn’t really work in my case unfortunately.
Now, for the problem:
I'm trying to create a route that will handle a get request and a post request which are sent to the route 'ellipses'.
These requests should receive and send data from and to an SQL database.
The problem is that for some reason the router is not ready to get these functions and gives me the error in the title:
Router.use () requires middleware function but got an undefined
Here is my code:
This code is from the file dat.js. its porpose is just to access the SQL database.
import { Sequelize } from "sequelize";
export const sequelize = new Sequelize('TheDataBaseName', 'TheUser', 'ThePassword', {
host: 'localhost',
dialect: 'mssql'
});
This code is from the file: controller.js. its porpose is to manage the requests and load the data.
import { sequelize } from "../dat";
export const sendEllipses = async (req, res, next) => {
try {
const ellipses = await getEllipsesFromJson();
return res.send(ellipses);
} catch (e) {
console.log(e);
}
};
export const addNewEllipse = async (req, res, next) => {
const { body: obj } = req;
let newEllipse;
try {
if (Object.keys(obj) !== null) {
logger.info(obj);
newEllipse = await sequelize.query(
`INSERT INTO [armageddon].[dbo].[ellipses] (${Object.keys(
obj
).toString()})
values (${Object.values(obj).toString()})`
);
} else {
console.log("the values are null or are empty");
}
return res.send(newEllipse);
} catch (error) {
console.log(error);
}
};
This code is on the file: routers.js.
its porpose is to define the route
import Router from "express";
import { sendEllipses } from "../ellipses.controller";
import { addNewEllipse } from "../ellipses.controller";
const router = Router();
export default router.route("/ellipses").get(sendEllipses).post(addNewEllipse);
This code is from the file: app.js. This is where everything actually happens.
import { router } from "../routers";
import express from "express";
app.use('/api', router);
app.listen(5000, () => {
console.log("server is runing on port 5000")
});
You need to export the router
const router = Router();
router.route("/ellipses").get(sendEllipses).post(addNewEllipse)
export default router
Now import the router:
import routes from "../router.js";
app.use('/api', routes);
Its also mentioned in the docs: https://expressjs.com/de/guide/routing.html#express-router

How Can I Serve Static Content Alongside Dynamic Routes in A Deno Oak Server

I am used to working with NodeJS and Koa. I've been playing with Deno and have run the example of a static fileserver:
/* static_server.js */
import { Application } from 'https://deno.land/x/oak/mod.ts'
const port = 8080
const app = new Application()
// Error handler middleware
app.use(async (context, next) => {
try {
await next()
} catch (err) {
console.error(err)
}
})
// Send static content
app.use(async (context) => {
console.log(`${context.request.method} ${context.request.url.pathname}`)
await context.send({
root: `${Deno.cwd()}/static`,
index: "index.html",
})
})
await app.listen({ port })
I have also created a dynamic server using routes:
/* routes.js */
import { Application, Router } from 'https://deno.land/x/oak/mod.ts'
const port = 8080
const app = new Application()
const router = new Router()
router.get('/', context => {
context.response.body = 'Hello world!'
})
router.get('/foo', context => {
context.response.body = 'Book Page'
})
router.get('/foo/:thing', context => {
context.response.body = `Foo ${context.params.thing}`
})
app.use(router.routes())
app.use(router.allowedMethods())
await app.listen({ port })
How can I combine these so that I can serve dynamic content but also provide static files such as the stylesheet?
In my Koa code I use the koa-static package:
import serve from 'koa-static'
app.use(serve('public'))
What is the equivalent for an Oak server?
Adding suggested code (thanks Jonas Wilms)
/* static_content.js */
import { Application, Router } from 'https://deno.land/x/oak/mod.ts'
const port = 8080
const app = new Application()
const router = new Router()
router.get('/', context => {
context.response.body = 'Hello world!'
})
router.get('/foo', context => {
context.response.body = 'Book Page'
})
router.get('/foo/:thing', context => {
context.response.body = `Foo ${context.params.thing}`
})
router.get(context => context.send({ root: `${Deno.cwd()}/static` }))
app.use(router.routes())
app.use(router.allowedMethods())
await app.listen({ port })
but this still does not work...
After combining a lot of the information in the comments I managed to get things working:
/* static_content.js */
import { Application, Router, Status } from 'https://deno.land/x/oak/mod.ts'
const port = 8080
const app = new Application()
const router = new Router()
// error handler
app.use(async (context, next) => {
try {
await next()
} catch (err) {
console.log(err)
}
})
// the routes defined here
router.get('/', context => {
context.response.body = 'Hello world!'
})
router.get('/error', context => {
throw new Error('an error has been thrown')
})
app.use(router.routes())
app.use(router.allowedMethods())
// static content
app.use(async (context, next) => {
const root = `${Deno.cwd()}/static`
try {
await context.send({ root })
} catch {
next()
}
})
// page not found
app.use( async context => {
context.response.status = Status.NotFound
context.response.body = `"${context.request.url}" not found`
})
app.addEventListener("listen", ({ port }) => console.log(`listening on port: ${port}`) )
await app.listen({ port })
I know I'm a bit late on the thread, but there are some things I would like to point out.
In Oak 10.1 (the current version at the time of this writing), the send function throws an error if the file it tired to load did not exist. Thus, our static+dynamic server can take on the following form.
import { oak, pathUtils } from './deps.ts'
const app = new oak.Application()
const router = new oak.Router()
app.use(async (ctx, next) => {
try {
await oak.send(ctx, ctx.request.url.pathname, {
root: 'static',
index: 'index.html',
})
} catch (_) {
await next()
}
})
router.get('/dynamic', ctx => {
ctx.response.body = 'dynamic route worked'
})
app.use(router.allowedMethods())
app.use(router.routes())
app.listen({ port: 8000 })
If you want to serve your static files at a certain root path, change the static middleware so that it checks for the root and then omits that root path from the second argument of the send function.
function staticMiddleware(rootPath: string, staticDirectory: string) {
return async (ctx, next) => {
if (!ctx.request.url.pathname.startsWith(rootPath)) return await next()
const newPath = ctx.request.url.pathname.slice(rootPath.length)
if (!newPath.startsWith('/') && newPath.length) return await next()
try {
await oak.send(ctx, newPath, {
root: staticDirectory,
index: 'index.html',
})
} catch (_) {
await next()
}
}
}
app.use(staticMiddleware('/assets', 'static'))
I think you should use the static router at last. Because when use static server first, dynamic router is nonreachable for static router error.
app.use(router.routes())
app.use(router.allowedMethods())
// move the static router down
app.use( async context => {
context.response.status = Status.NotFound
context.response.body = `"${context.request.url}" not found`
})
Not sure whether this is still relevant or already outdated, but as of now (August 2022), there seems to be no general answer to this.
Serving Static Files Alongside Your API Using Oak/Deno
When setting up OpenAPI for an oak-based REST service, I was coming across this issue as well. Requirements were:
Serve openapi.yml statically from /openapi/openapi.yml
Serve a HTML statically from /openapi for convenience
Serve prefixed routers unaffectedly
A straight-forward approach to serve static files from a certain directory under a sub-path of the application is using a middleware and checking the path:
import {
Application, Context, Router
} from 'https://deno.land/x/oak#v11.1.0/mod.ts';
const app = new Application();
const port = 3000;
// Middleware only hooking in and sending static files if prefix matches
// the desired subpath:
const openapi = async (ctx: Context, next: () => Promise<unknown>) => {
const prefix = '/openapi'; // Sub-path to react on
if (ctx.request.url.pathname.startsWith(prefix)) {
await ctx.send({
root: `${Deno.cwd()}/src/openapi/`, // Local directory to serve from
index: 'index.html',
path: ctx.request.url.pathname.replace(prefix, ''), // Map to target path
});
} else {
// If the request does not match the prefix, just continue serving from
// whatever comes next..
await next();
}
};
// This is a dummy API endpoint wrapped into a prefixed router for demo:
const statusRouter = new Router({ prefix: '/status' });
statusRouter.get('/', (ctx: Context) => {
ctx.response.body = {
healthy: true,
ready: true,
};
});
// Boilerplate..
app.use(openapi);
app.use(statusRouter.routes());
app.use(statusRouter.allowedMethods());
app.addEventListener('listen', () => {
console.log(`Listening on localhost:${port}`);
});
await app.listen({ port });
Running this MWE using deno run --allow-net --allow-env --allow-read src/serve.ts, you'll
find the statically served /openapi/openapi.yml,
find the index.html from your local static path served under /openapi (resp. /openapi/ and /openapi/index.html)
find the /status API behaving just normally.
i'm using like that. In html you can provide a path to your file:
<script src="/js/app.js"></script>
then you can use routes to provide what do you want to use on path js/app.js:
import {RouterContext} from 'https://deno.land/x/oak/mod.ts'
const decoder = new TextDecoder("utf-8")// set doecoder
const fileCont = await Deno.readFile('./views/test.js') //getting file conetent
const fileJS = decoder.decode(fileCont) //decoding it
router.get('/js/app.js', (ctx: RouterContext) => { //yep, route can has defferents of real file location
ctx.response.type = "application/javascript"
ctx.response.body = fileJS
})
and whatever you are providing this link somewhere it'll render you file.
Deno REST API

Engine not found for the ".js" file extension

I want to use koa-views with Koa and Koa-Router with Next.js. In previous projects, I had no issues with express but in this project, I have to use Koa. Using its router, I want to render a page: /some/page/:id. Following the same Nextjs way:
router.get('/some/page/:id', async (ctx, next) => {
const actualPage = '/some/page/id' // id.js (not actual name 😝)
await ctx.render(actualPage, {/* could pass object */})
});
That would work if I was using express. With Koa:
const Koa = require('koa');
const views = require('koa-views');
// const render = require('koa-views-render'); <-- I what's this?
[..] // Making things short here
const server = new Koa();
const router = new Router();
// My issue, I'm seeing tutorials using other engines: .ejs etc
// I'm not using any, I only have .js files
server.use(views(__dirname + "/pages", { extension: 'js' }));
Using the same router.get... function as above, I get:
Error: Engine not found for the ".js" file extension
When I go to /some/page/123, I'd expect it to render the file /pages/some/page/id.js. How?
It turns out I do not need any extra modules to achieve this 🙀
Create a function called, ie, routes then pass app and router as a param
const routes = (router, app) => {
router.get('/some/page/:id', async (ctx) => {
const { id } = ctx.params
const actualPage = '/some/page/id'
// Render the page
await app.render(ctx.req, ctx.res, actualPage, {foo: 'Bar'})
}
}
module.exports = routes
Inside your server.js file:
// const routes = require('./routes);
// const app = next({ dev }); // import other modules for this section
// app.prepare().then(() => {
// const router = new Router();
// [..]
// routes(router, app)
// })
The commented out section is a slim down version to make a point in where things should be.

Axios get request keeps hitting 404

This is the get request:
this.props.userId contains the userId.
componentDidMount() {
axios.get('/api/auth/booked/' + this.props.userId)
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
});
}
This is the routes on my backend:
router.get('/booked:id', UserController.bookedClasses);
It's something to do with the '/booked:id'
Result:
GET http://localhost:3000/api/auth/booked/5bdb18071c8fb30d31969aef 404 (Not Found)
Nice and simple but for some odd reason, I can't get a response, I have all my system working apart from this route, can anyone spot anything that shouldn't be there?
Any feedback would be appreciated to help me and others!
This is my routes folder which holds all my routes:
const express = require('express');
const router = express.Router();
const UserController = require('../controllers/auth');
router.post('', UserController.createUser);
router.post('/login', UserController.login);
router.post('/bookclass', UserController.bookClass);
router.get('/:id', UserController.getUser);
router.get('/booked:id', UserController.bookedClasses);
module.exports = router;
Router params must be specified in the path of the route. Example:
'/some/route/:param'
or with multiple params:
'/some/route/:param/:anotherParam'
In your example:
router.get('/booked:id', UserController.bookedClasses);
should be (check the extra / in the path):
router.get('/booked/:id', UserController.bookedClasses);

Categories

Resources