Koa.js - serving static files and REST API
Asked Answered
P

5

12

I'm new to koa.js library and I need some help. I'm trying to make simple REST application using koa. I have a static html and javascript files I want to serve on route / and REST API accessing from /api/.

This is my project directory tree:

project
├── server
│   ├── node_modules
│   ├── package.json
│   └── src
│       ├── config
│       ├── resources
│       └── server.js
├── ui
│   ├── app
│   ├── bower.json
│   ├── bower_components
│   ├── dist
│   ├── node_modules
│   ├── package.json
│   └── test

This is my source:

var app = require('koa')();
app.use(mount('/api/places', require('../resources/places')));

// does not work
var staticKoa = require('koa')();
staticKoa.use(function *(next){
  yield next;
  app.use(require('koa-static')('../ui/app', {}));
});
app.use(mount('/', staticKoa));

// does not work
app.use(mount('/', function*() {
    app.use(require('koa-static')('../ui/app/', {}));
}));

// does not work
app.use(mount('/', function*() {
    app.use(require('koa-static')('.', {}));
}));
// GET package.json -> 404 not found

I've tried koa-static, koa-static-folder, koa-static-server libraries and neither works so I'm doing something wrong.

I've tried this and it works, but I don't have access to my REST api:

var app = require('koa')();
app.use(require('koa-static')('../ui/app/', {}));
Plagiary answered 22/9, 2015 at 15:47 Comment(2)
Does my answer make sense, do you have other questions?Remanent
I forgot yo upvote your answer. Almost an year later, thank you :)Plagiary
R
24

It was a little hard for me to follow what you were doing in your example code... Here is a simple example that does everything your wanting:

'use strict';
let koa     = require('koa'),
    send    = require('koa-send'),
    router  = require('koa-router')(),
    serve   = require('koa-static');

let app = koa();
// serve files in public folder (css, js etc)
app.use(serve(__dirname + '/public'));

// rest endpoints
router.get('/api/whatever', function *(){
  this.body = 'hi from get';
});
router.post('/api/whatever', function *(){
  this.body = 'hi from post'
});

app.use(router.routes());

// this last middleware catches any request that isn't handled by
// koa-static or koa-router, ie your index.html in your example
app.use(function* index() {
  yield send(this, __dirname + '/index.html');
});

app.listen(4000);
Remanent answered 22/9, 2015 at 16:57 Comment(9)
What is the pointer this in the ` this.body = 'hi from get'; `?Underpin
I've been wrestling with this for koa v2, maybe you can provide some insight? app.use(async (ctx, next) => { await send(ctx, path.join(__dirname, '../', 'client/index.html')) }) I get a 404 not found error for some reason... Although with a bit of a hack I can serve the individual html file, if it isn't the folder, I'm not able to load my images, etc.Bautista
@Bautista Did you ever come up with an answer? I tried app.use(async (ctx, next)... and wasn't able to get that to work. Tried various different variants but nothing seems to work.Arithmetic
@Arithmetic I've got a really hacky solution from another thread I found here: #24025066Bautista
@Bautista Through trial and error, I was able to get this working last night: app.use(async function (ctx, next) { return send(ctx, '/index.html', { root: paths.client() }).then(() => next()) }) The key thing was to specify {root:<some path>}. I think the problem in my case was that for security reasons, send doesn't allow relative paths or paths outside of the project tree. Specifying the root param and then giving a filename relative to that seemed to fix the problem. I guess I expected koa-send to log an error/warning on the node output about this.Arithmetic
FWIW, I've run into so many problems with this koa vs koav2 thing, that I'm seriously considering porting all my stuff to express. Koa examples on the net seem to be fragmented among koa and koa v2, making it hard to get any example code working.Arithmetic
@Arithmetic nice job! I'm going to have to give this a shot. A lot of the time I find myself referencing Express examples and porting them to Koa. I love the async/await style though, and would really enjoy Koa2 being widely adopted. I feel your frustrations as well.Bautista
what is the difference between koa-send and koa-static here? why do we need both of them? Can't we serve files from '/public' with koa-send? Thanks!Carlicarlick
@hoodsy, I am using app.use(async (ctx, next) => await ctx.send(`${__dirname}/public/index.html`)) and it works fine.Burgomaster
E
5
const root = require('path').join(__dirname, 'client', 'build');
app.use(serve(root));

app.use(async ctx => {
    await send(ctx, `/index.html`, {
        root
    });
});
Exeter answered 2/4, 2020 at 7:32 Comment(1)
Code only answers can almost always be improved by adding explanation of how and why they work.Flavorsome
B
3

From @Nurpax in the comments:

app.use(async function (ctx, next) {
  return send(ctx, '/index.html', { root: paths.client()
})
.then(() => next()) }) 

The key thing was to specify {root:<some path>}. I think the problem in my case was that for security reasons, send doesn't allow relative paths or paths outside of the project tree. Specifying the root param and then giving a filename relative to that seemed to fix the problem. I guess I expected koa-send to log an error/warning on the node output about this.

Bautista answered 3/9, 2016 at 13:53 Comment(1)
Have no idea why did you put promises in code. Just use await :) app.use(async (ctx) => { await send(ctx, '/index.html', { root: STATIC_FOLDER_PATH, }); });Xenos
H
0

This piece of code is working fine for me.

I'm serving auto files images and videos in koa.

const koa = require('koa');
const koaRouter = require('koa-router');
const cors = require('@koa/cors');
const koaBody = require('koa-bodyparser');
const koaSend = require('koa-send'); 
const serve = require('koa-static');
const app = new koa();
const router = new koaRouter();

router.get('/public/audios/:item', async ctx => {
    const { item } = ctx.params;
    await koaSend(ctx, { root: `./public/audios/${item}` });
});

router.get('/public/videos/:item', async ctx => {
    const { item } = ctx.params;
    await koaSend(ctx, { root: `./public/videos/${item}` });
})

router.get('/public/images/:item', async ctx => {
    const { item } = ctx.params;
    await koaSend(ctx, { root: `./public/images/${item}` });
})

app
  .use(serve('./public'))
  .use(koaBody({ jsonLimit: '100mb', formLimit: '100mb', textLimit: '100mb' }))
  .use(cors())
  .use(router.routes())
  .use(router.allowedMethods())

app.listen(3000);
Holmun answered 3/9, 2021 at 20:22 Comment(0)
B
0

What I ended up doing:

import * as fs from "fs";

myrouter.get("/data/:name", async (ctx, next) => {
    ctx.body = fs.createReadStream(`./data/${ctx.params.name}`);
});

However be aware that entering path/../../../some/path can bypass the root path.

Bjork answered 8/3 at 15:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.