At the moment only server routes can accept POST requests. This is a bit of a nuisance if you want to e.g. handle a form submission without redirecting (which creates new request/response objects, which means you need to persist the posted data).
I wonder if there should be some way for pages to handle POST. Alternatively, if it was possible to server routes and pages to shadow each other, we could do something like this:
routes/form-submit.js # accepts the POST, calls next
routes/form-submit.html # populates the response
Ideally, I would be able to end an api request conceptually like:
res.write(serverSideRender(require("../thanks.html"), { someData: 3 }))
_Edit_
serverSideRender should probably be async. The question is how preload should behave. Ideally you want to be able to pass data to such a page _and_ trigger the preload hook. However, I would like to avoid passing the data as params or query as that would make the page sensitive to XSS attack (e.g. sharing links like http://mydomain.com/thank.html?message="thank you!<script>alert(3)</script>"
Yeah, it'd make sense to expose that. Shouldn't even be that difficult — we're almost there with
import Page from '../thanks.html';
export function post(req, res) {
// do some things
const { html, head, css } = Page.render({ my: 'data' });
res.end(html); // close — just missing layout and CSS
}
Just need to wrap it all up in a function that wraps the HTML in the page template, supports preload, and serves the 5xx page in case of errors:
import Page from '../thanks.html';
import { render } from 'sapper/server-runtime.js'; // open to bikeshedding
export async function post(req, res) {
// do some things
const html = await render(Page, { my: 'data' });
res.end(html);
}
just for clarification, would {my: data} be merged with what gets passed to payload, to the page component, or both?
I just ran into this problem. I'm not sure I'm following the difference between "server side routes" and something like routes/[email].json.js.
Does the latter not support something like export async function post...?
NM - this was actually a stupid and unrelated bug... still interested in resolving this issue, though
I'm trying to use sapper to develop something that (for now) will be deployed to netlify with export. This is a hangup for me - netlify handles forms specially by noticing a "netlify" tag in the form which it will automatically rewrite to hit it's back-end.
Right now, I'm experiencing a strange error where no matter how I specify action, I get:
4.39 kB contact/index.html
3.54 kB (404) contact/index.html/index.html
> EEXIST: file already exists, mkdir 'C:\Users\davcl\Projects\bead.glass\__sapper__\export\contact\index.html'
Which I don't quite understand. I've tried having no action, and making action "", "/contact", and "/dummy" (with a dummy.js file). All give the same error.
EDIT: I've illustrated that this would be a pretty easy change to sapper in the PR #770 ... not sure that it's a good change though :-P
Hi, I'm trying to figure out form posting as well. As per #634, the use of next() now works to pass through from the post handler to the page, so this is roughly working. My setup goes along the following lines:
x.svelte and x.js are in the same dir inside routes;x.svelte has an HTML form that POSTs to /x;x.js exports a post(req, res, next) function that does the following:res.end() (so no headaches with browser refreshing).next() to re-render with svelte.The only real issue with this workflow that can't be worked around at present is roughly what is identified above: that there is no way to pass through submitted/sanitised data and error messages to x.svelte from the post() function (i.e. in the 3.ii case).
This could be solved with something super-simple like having a data structure attached to req by the sapper.middleware, and that gets passed through into preload:
~~~ js
async function post(req, res, next) {
// Initially req.sapperPreloadData = {}
const email = await doSomethingToParsePostData(req);
Object.assign(req.sapperPreloadData, {
email,
emailError: "This email is already taken!"
});
next();
}
~~~
And:
~~~ html
~~~
Another way to do this that doesn't require patching Sapper at all is to attach the data as a non-enumerable symbol-keyed property of the req.query object. Using a symbol means that there is no possibility of a clash with any HTTP-supplied query params; and non-enumerability means that the value won't break devalue serialization of query object.
~~~ javascript
export async function post(req, res, next) {
const email = await doSomethingToParsePostData(req);
Object.defineProperty(req.query, Symbol.for('sapper.data'), {
enumerable: false,
value: {
email,
emailError: "This email is already taken!"
}
});
next();
}
~~~
and:
~~~ html
~~~
It's hacky but at least it works ...
This will now be handled in kit :)
You can use Post/Redirect/Get pattern.
Post data to .js file
Redirect it to svelte with required get params
Serve request from svelte
Most helpful comment
Yeah, it'd make sense to expose that. Shouldn't even be that difficult — we're almost there with
Just need to wrap it all up in a function that wraps the HTML in the page template, supports
preload, and serves the 5xx page in case of errors: