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