mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
100 lines
3.1 KiB
Markdown
100 lines
3.1 KiB
Markdown
|
# Running Next.JS and React /inside/ of ActionHero
|
||
|
|
||
|
This server will render dynamic next.js/react pages on some routes, and normal ActionHero API requests on others.
|
||
|
This configuration works with both Next and ActionHero hot reloading of code.
|
||
|
|
||
|
A more detailed example showcasing how to use fetch and web sockets to interact with your API can be found here: https://github.com/actionhero/next-in-actionhero
|
||
|
|
||
|
## To install:
|
||
|
(assuming you have [node](http://nodejs.org/) and NPM installed)
|
||
|
|
||
|
`npm install`
|
||
|
|
||
|
## To Run:
|
||
|
`npm start`
|
||
|
|
||
|
|
||
|
## How does this work?
|
||
|
|
||
|
1. Create an initializer to load next.js and create a handler that can extract the normal node `req` and `res` from the connection
|
||
|
|
||
|
```js
|
||
|
// initializers/next.js
|
||
|
|
||
|
const {Initializer, api} = require('actionhero')
|
||
|
const next = require('next')
|
||
|
|
||
|
module.exports = class NextInitializer extends Initializer {
|
||
|
constructor () {
|
||
|
super()
|
||
|
this.name = 'next'
|
||
|
}
|
||
|
|
||
|
async initialize () {
|
||
|
api.next = {
|
||
|
render: async (connection) => {
|
||
|
if (connection.type !== 'web') { throw new Error('Connections for NEXT apps must be of type "web"') }
|
||
|
const req = connection.rawConnection.req
|
||
|
const res = connection.rawConnection.res
|
||
|
return api.next.handle(req, res)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
api.next.dev = (api.env === 'development')
|
||
|
if (api.next.dev) { api.log('Running next in development mode...') }
|
||
|
|
||
|
api.next.app = next({dev: api.next.dev})
|
||
|
api.next.handle = api.next.app.getRequestHandler()
|
||
|
await api.next.app.prepare()
|
||
|
}
|
||
|
|
||
|
async stop () {
|
||
|
await api.next.app.close()
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
2. Create an action which will run the above `api.next.render(connection)`. Note that we will not be relying on ActionHero to respond to the client's request in this case, and leave that up to next (via: `data.toRender = false`)
|
||
|
|
||
|
```js
|
||
|
// actions/next.js
|
||
|
|
||
|
const {Action, api} = require('actionhero')
|
||
|
|
||
|
module.exports = class CreateChatRoom extends Action {
|
||
|
constructor () {
|
||
|
super()
|
||
|
this.name = 'render'
|
||
|
this.description = 'I render the next.js react website'
|
||
|
}
|
||
|
|
||
|
async run (data) {
|
||
|
data.toRender = false
|
||
|
return api.next.render(data.connection)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
```
|
||
|
|
||
|
3. Tell ActionHero to use the api rather than the file server as the top-level route in `api.config.servers.web.rootEndpointType = 'api'`. This will allows "/" to listen to API requests. Also update `api.config.general.paths.public = [ path.join(__dirname, '/../static') ]`. In this configuration, the next 'static' renderer will take priority over the ActionHero 'public file' api. Note that any static assets (CSS, fonts, etc) will need to be in "./static" rather than "./public".
|
||
|
|
||
|
Note that this is where the websocket server, if you enable it, will place the `ActionheroWebsocketClient` libraray.
|
||
|
|
||
|
4. Configure a wild-card route at the lowest priority of your GET handler to catch all web requests that aren't caught by other actions:
|
||
|
|
||
|
```js
|
||
|
// config/routes.js
|
||
|
|
||
|
exports['default'] = {
|
||
|
routes: (api) => {
|
||
|
return {
|
||
|
get: [
|
||
|
{ path: '/time', action: 'time' },
|
||
|
|
||
|
{ path: '/', matchTrailingPathParts: true, action: 'render' }
|
||
|
]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|