I like Sandstorm so much that I’m finally developing an app for it. It’ll use PostGraphQL to move all of the business logic into PostgreSQL, and React, Redux, and Apollo to handle the UI and data reading/manipulation. I’ll be documenting the weird issues I’ve run into when setting this up, since there aren’t a ton of docs on this particular combo of services.
PostgreSQL
initdb
Due to how grains have random UID/GID assigned to them for security purposes,
and because /etc/passwd
and /etc/group
are not available when running launcher.sh
,
initdb
will fail because it wants to look up the current UID in /etc/passwd
for security reasons. You can get around this by using
nss_wrapper
:
export PGDATA=/var/lib/postgres
echo "postgres:x:$(id -u):$(id -g):PostgreSQL user:/var/lib/postgres:/bin/sh" > /var/passwd
echo "postgres:x:$(id -g):" > /var/group
if [ ! -d $PGDATA ]; then
LD_PRELOAD=/usr/lib/libnss_wrapper.so NSS_WRAPPER_PASSWD=/var/passwd NSS_WRAPPER_GROUP=/var/group /usr/lib/postgresql/10/bin/initdb
fi
localhost
Since you don’t have /etc
. you also don’t have /etc/hosts
, which is where
the hostname localhost
is defined. Use 127.0.0.1
everywhere (your app,
PostgreSQL config) instead.
NodeJS
HOME
Some Node modules want your HOME
directory:
/usr/lib/node_modules/npm/node_modules/update-notifier/node_modules/xdg-basedir/index.js:5
const home = os.homedir();
^
Error: ENOENT: no such file or directory, uv_os_homedir
export HOME=/var
at the top of launcher.sh
will fix it.
Apollo Client, and all XHR for that matter
You’ll want to send cookies from the grain frame
along with your request, otherwise XHR won’t work. In an Apollo client for
GraphQL requests, that looks like this (bonus Authentication
header
setting for PostGraphQL):
import { ApolloClient } from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { setContext } from 'apollo-link-context';
const httpLink = createHttpLink({ uri: '/graphql' });
const authLink = setContext((_, { headers }) => {
const { jwtToken } = place.where.jwt.token.stored;
const newHeaders = {};
if (jwtToken) {
newHeaders.authorization = `Bearer ${jwtToken}`;
}
return {
headers: {
...headers,
...newHeaders,
},
credentials: 'include', // gets XHR working with Sandstorm
};
});
export default new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
});