Custom ReasonML Server (#6970)
- A typesafe custom server built in reasonml.
This commit is contained in:
parent
ca565fc6e4
commit
9c8d7290df
9 changed files with 215 additions and 0 deletions
26
examples/custom-server-reasonml/.gitignore
vendored
Normal file
26
examples/custom-server-reasonml/.gitignore
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
*.exe
|
||||
*.obj
|
||||
*.out
|
||||
*.compile
|
||||
*.native
|
||||
*.byte
|
||||
*.cmo
|
||||
*.annot
|
||||
*.cmi
|
||||
*.cmx
|
||||
*.cmt
|
||||
*.cmti
|
||||
*.cma
|
||||
*.a
|
||||
*.cmxa
|
||||
*.obj
|
||||
*~
|
||||
*.annot
|
||||
*.cmj
|
||||
*.bak
|
||||
lib/bs
|
||||
*.mlast
|
||||
*.mliast
|
||||
.vscode
|
||||
.merlin
|
||||
.bsb.lock
|
30
examples/custom-server-reasonml/README.md
Normal file
30
examples/custom-server-reasonml/README.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
# Custom server REASONML
|
||||
|
||||
# Install it and run:
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
# or
|
||||
yarn
|
||||
yarn dev
|
||||
```
|
||||
|
||||
# Build the app
|
||||
```bash
|
||||
yarn next:build
|
||||
npm run next:build
|
||||
```
|
||||
|
||||
# Run the production app
|
||||
Run this command after yarn build.
|
||||
```bash
|
||||
yarn start
|
||||
```
|
||||
|
||||
# The idea behind this example
|
||||
ReasonML is an exciting new language and since it can compile directly to JS via bucklescript
|
||||
that means that we can power our backend server with REASONML and also have the frontend built with
|
||||
reasonreact, which is covered in another [example](https://github.com/zeit/next.js/tree/canary/examples/with-reasonml).
|
||||
This example shows how powerful & helpful it can be to build a next js custom server with a typesafe language.
|
||||
|
||||
The example has been built off the `custom-server` example that uses pure `nodejs` to build the custom server.
|
14
examples/custom-server-reasonml/bsconfig.json
Normal file
14
examples/custom-server-reasonml/bsconfig.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "custom-server-reason",
|
||||
"version": "0.1.0",
|
||||
"sources": {
|
||||
"dir": "server",
|
||||
"subdirs": true
|
||||
},
|
||||
"package-specs": {
|
||||
"module": "commonjs",
|
||||
"in-source": true
|
||||
},
|
||||
"suffix": ".bs.js",
|
||||
"bs-dependencies": []
|
||||
}
|
26
examples/custom-server-reasonml/package.json
Normal file
26
examples/custom-server-reasonml/package.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "custom-server-reason",
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"clean": "bsb -clean-world",
|
||||
"build": "bsb -make-world",
|
||||
"watch": "bsb -make-world -w",
|
||||
"next-build": "next build",
|
||||
"dev": "bsb -make-world && node server/server.bs.js",
|
||||
"next:build": "bsb -make-world && next build",
|
||||
"start": "NODE_ENV=production node server/server.bs.js"
|
||||
},
|
||||
"keywords": [
|
||||
"BuckleScript"
|
||||
],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"bs-platform": "^4.0.18"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "^8.0.4",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
3
examples/custom-server-reasonml/pages/a.js
Normal file
3
examples/custom-server-reasonml/pages/a.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import React from 'react'
|
||||
|
||||
export default () => <div>a</div>
|
3
examples/custom-server-reasonml/pages/b.js
Normal file
3
examples/custom-server-reasonml/pages/b.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import React from 'react'
|
||||
|
||||
export default () => <div>b</div>
|
17
examples/custom-server-reasonml/pages/index.js
Normal file
17
examples/custom-server-reasonml/pages/index.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
|
||||
export default () => (
|
||||
<ul>
|
||||
<li>
|
||||
<Link href='/b' as='/a'>
|
||||
<a>a</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href='/a' as='/b'>
|
||||
<a>b</a>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
)
|
35
examples/custom-server-reasonml/server/http.re
Normal file
35
examples/custom-server-reasonml/server/http.re
Normal file
|
@ -0,0 +1,35 @@
|
|||
type http;
|
||||
|
||||
[@bs.deriving abstract]
|
||||
type parsedObjectUrl = {
|
||||
pathname: string,
|
||||
query: string,
|
||||
};
|
||||
|
||||
module Request = {
|
||||
type t;
|
||||
[@bs.get] external url: t => string = "";
|
||||
[@bs.get] external method_: t => string = "method";
|
||||
};
|
||||
|
||||
module Response = {
|
||||
type t;
|
||||
[@bs.send.pipe: t] external end_: 'a => unit = "end";
|
||||
[@bs.send] external setHeader: (t, string, string) => unit = "";
|
||||
[@bs.send] external writeHead: (t, int) => unit = "";
|
||||
let setHeader = (header: string, value: string, response: t) => {
|
||||
setHeader(response, header, value);
|
||||
response;
|
||||
};
|
||||
let writeHead = (status: int, response: t) => {
|
||||
writeHead(response, status);
|
||||
response;
|
||||
};
|
||||
};
|
||||
|
||||
[@bs.module "http"]
|
||||
external createServer: ((Request.t, Response.t) => unit) => http = "";
|
||||
|
||||
[@bs.send.pipe: http] external listen: (int, string => unit) => unit = "";
|
||||
|
||||
[@bs.module "url"] external parse: (string, bool) => parsedObjectUrl = "";
|
61
examples/custom-server-reasonml/server/server.re
Normal file
61
examples/custom-server-reasonml/server/server.re
Normal file
|
@ -0,0 +1,61 @@
|
|||
open Http;
|
||||
|
||||
[@bs.deriving abstract]
|
||||
type nextparams = {dev: bool};
|
||||
|
||||
type nextoutput;
|
||||
[@bs.send] external prepare: nextoutput => Js.Promise.t(unit) = "";
|
||||
|
||||
[@bs.send]
|
||||
external getRequestHandler:
|
||||
(nextoutput, unit) => (. Request.t, Response.t, parsedObjectUrl) => unit =
|
||||
"";
|
||||
|
||||
[@bs.send]
|
||||
external render: (nextoutput, Request.t, Response.t, string, string) => unit =
|
||||
"";
|
||||
|
||||
[@bs.module] external next: nextparams => nextoutput = "next";
|
||||
|
||||
[@bs.val] [@bs.scope ("process", "env")]
|
||||
external port: Js.Nullable.t(int) = "PORT";
|
||||
|
||||
[@bs.val] [@bs.scope ("process", "env")]
|
||||
external node_env: string = "NODE_ENV";
|
||||
|
||||
[@bs.val] external parseInt: (int, int) => int = "parseInt";
|
||||
let port =
|
||||
switch (Js.Nullable.toOption(port)) {
|
||||
| Some(port) => port
|
||||
| None => 3000
|
||||
};
|
||||
let is_dev = node_env != "production";
|
||||
|
||||
let nxt = nextparams(~dev=is_dev);
|
||||
let app = next(nxt);
|
||||
|
||||
let handle = getRequestHandler(app, ());
|
||||
|
||||
Js.Promise.(
|
||||
prepare(app)
|
||||
|> then_(() => {
|
||||
createServer((req, res) => {
|
||||
let parsedUrl = parse(Request.url(req), true);
|
||||
let pathname = pathnameGet(parsedUrl);
|
||||
let query = queryGet(parsedUrl);
|
||||
|
||||
switch (pathname) {
|
||||
| "/a" => app->render(req, res, "/a", query)
|
||||
| "/b" => app->render(req, res, "/b", query)
|
||||
| _ => handle(. req, res, parsedUrl)
|
||||
};
|
||||
})
|
||||
|> listen(port, _err =>
|
||||
print_string(
|
||||
"> Listening on port http://localhost:" ++ string_of_int(port),
|
||||
)
|
||||
);
|
||||
|
||||
resolve(print_string(""));
|
||||
})
|
||||
);
|
Loading…
Reference in a new issue