For applications meant to use the same space consistently, open an existing
space each time Flatfile is opened. This suits situations where consistently
editing a dataset is preferred.
If you have already tried our Embed a New Space guide you will
notice this guide departs heavily, so you will want to create this in a new
directory, as translation would be more difficult than creating from scratch.
To complete this tutorial, you'll need to retrieve your
Secret key
from your development environment.
Note: Unlike the Publishable Key, the Secret Key shouldn’t be passed through
the browser as it will have full access. This is why we are showing this
example in a server-side call.
Make a new Directory.
mkdir example-flatfile-vuejs-embed
Go into that directory.
cd example-flatfile-vuejs-embed
Follow prompts from the init command.
npm init
Install Packages
npm i @flatfile/api @flatfile/listener @flatfile/plugin-record-hook @flatfile/vue dotenv ejs express vue && npm i --save-dev @types/express @types/node @vitejs/plugin-vue concurrently nodemon ts-node typescript vite vue-tsc
The file structure of this application is fairly complex, as it requires a
dedicated server and API. You can create an api endpoint externally if desired
to keep your own application less complex, however this application is an
all-in-one application server and api solution.
In order to create this solution, create a file structure like the following:
├── server/ ├── assetsRouter.ts ├── homepageRouter.ts └── index.ts├── src/ ├── components/ ├── ExistingSpace.vue ├── Home.vue └── NewSpace.vue <--- optional, requires listener.ts and config.ts ├── listeners/ └── listener.ts <--- optional depending on if NewSpace.vue is included ├── styles/ └── styles.css ├── workbooks/ └── config.ts <--- optional depending on if NewSpace.vue is included ├── App.vue ├── main.ts ├── shims-vue.d.ts <--- may be optional └── vite-env.d.ts├── views/ └── index.html.ejs├── .env├── .gitignore├── nodemon.json├── package.json <--- already created├── package-lock.json <--- already created├── tsconfig.json└── vite.config.js
In this file structure, you will primarily work out of /src especially in
/src/components. Many of thsese files are configuration files which are mostly
set once and not touched again. So while there may be a lot, they won’t be
actively managed.
Update your .env. FLATFILE_API_KEY is your Secret Key and SPACE_ID is the
Space you want to open in the importer. This can be found on your Dashboard
where it lists your Spaces. You may also want to include your PUBLISHABLE_KEY
and ENVIRONMENT_ID if you want to have your app create new spaces as well.
Note in the below example some of the variables are prefixed with VITE_. This
is because Vite requires the prefix to access them at runtime. The
FLATFILE_API_KEY should never be accessible from the browser for security
reasons, and should not have this prefix.
# Required for creating new spacesVITE_PUBLISHABLE_KEY = "pk_12345"VITE_ENVIRONMENT_ID = "us_env_12345"# Required for embedding existing spacesVITE_SPACE_ID = "us_sp_12345"FLATFILE_API_KEY = "sk_12345"
Before starting to build the application, it is important to note that there are
a few moving parts, and it won’t be able to start up between each update that is
being made. This guide will endeavor to break out files into groups, so that
after everything is updated as directed, the app should start up without errors.
This app has a home page to route the user to create a new space or embed an
existing space. While the former choice is optional and not the focus of this
guide (although instructions on how to include this function will be included),
the files edited here set up functionality for embedding existing spaces, and as
such is still necessary.
This application utilizes ejs, which helps build dynamic html pages. This allows
us to dynamically create both development and production environment outputs
from one file. Set this up at /views/index.html.ejs
This file wil not work before the server is functioning, but is required for the
rest of the app to work.
Next lets create the server that will act as the backend of the application.
This will be necessary to serve the pages as well as get the existing space, as
due to security reasons the Secret Key cannot be exposed to the browser at any
time.
server/index.ts
import dotenv from 'dotenv';import express from "express";import path from "path";import { FlatfileClient } from "@flatfile/api";dotenv.config();const port = process.env.PORT || 3000;const publicPath = path.join(path.resolve(), "public"); //If your project doesn't require a public path this may not be necessaryconst distPath = path.join(path.resolve(), "dist");app.get('/api/spaces/:id', async (_req, res)=>{ const {id} = _req.params; const flatfile = new FlatfileClient({ token: process.env.FLATFILE_API_KEY, environment: 'https://platform.flatfile.com/v1/', }); try { const space = await flatfile.spaces.get(id); res.json({ space }); } catch (error) { console.error("Error retrieving space:", error); res.status(500).json({ error: "Failed to retrieve space" }); }})app.listen(port, () => { console.log("Server listening on port", port);});
In addition to the server/index.ts, you will need to create two routers - One
for the homepage and one for the assets. This is because the frontend runs on a
different port than you’ll be accessing the app from, so the app will need a bit
of help resolving pathing.
Create server/homepageRouter.ts and server/assetsRouter.ts as follows, then
update server/index.ts.
Before building the components, you’ll need to make a couple TypeScript
Declaration files. This is so your IDE won’t throw unnecessary errors at you
during development.
You’ll need a src/vite-env.d.ts file and a src/shims-vue.d.ts file in order
to sort your the declarations. Create them as follows:
/// <reference types="vite/client" />
Next you’ll need a src/main.ts file to mount src/App.vue to your HTML.
Create it and your App Component as shown below.
This app is built around a Home component that is loaded first and lets you
navigate the app. That component is fairly basic and should look like this:
src/components/Home.vue
<template> <h3>Choose what you want to do!</h3> <div class="button-container"> <a href="#/existing-space">Embed a Existing Space</a> </div></template>
Now you’ll need to build the component that will get your space and return it to
the browser.
The component should end up looking something like this:
You’ll need to update your App.vue to include the existing space component. It
should end up looking like the example below:
src/App.vue
<template> <div class="main"> <h2> <code><Flatfile /></code> </h2> <p>Embed Flatfile in just a few lines of code.</p> <nav class="navbar"> <a href="#/">Home</a> <a href="#/existing-space">Embed a Existing Space</a> <component :is="currentView" /> </nav> </div></template><script setup>import { ref, computed } from 'vue'import Home from './components/Home.vue'import ExistingSpace from './components/ExistingSpace.vue'const routes = { '/': Home, '/existing-space': ExistingSpace,}const currentPath = ref(window.location.hash);window.addEventListener('hashchange', () => { currentPath.value = window.location.hash});const currentView = computed(() => { return routes[currentPath.value.slice(1) || '/'] || NotFound});</script>
It may be wise to set up your styles in src/styles/styles.css. These should
end up looking like the example below:
src/styles/styles.css
/* Styles for home page */html,body { height: 100%; margin: 0; padding: 0; font-family: sans-serif; background: #090b2b; color: #fff;}#app { display: flex; align-items: center; justify-content: center; height: 100%;}/* End of styles for home page */:root { --ff-primary-color: #4c48ef !important; --ff-secondary-color: #616a7d !important; --ff-text-color: #090b2b !important; --ff-dialog-border-radius: 4px !important; --ff-border-radius: 5px !important; --ff-bg-fade: rgba(0, 0, 0, 0.2) !important;}nav {}nav a { color: white; margin: 1em;}.button-container a { border: rgb(101, 201, 101) 1px solid; border-radius: 15px; padding: 0.5em; background-color: rgb(101, 201, 101); text-decoration: none; transition: 0.25s;}.button-container a:hover { border: rgb(56, 139, 56) 1px solid; border-radius: 15px; padding: 0.5em; background-color: rgb(56, 139, 56); text-decoration: none;}.new-space-button-container { margin-top: 1em;}/* The default mount element *//* #flatfile_iFrameContainer {} *//* A div around the iframe that contains Flatfile *//* .flatfile_iframe-wrapper {} *//* The actual iframe that contains Flatfile *//* #flatfile_iframe {} */.flatfile-close-button { display: none !important;}/* Begin style overrides for when Flatfile is displayed as a modal *//* This class gets appended to the flatfile_iframe-wrapper div */.flatfile_displayAsModal { padding: 50px !important; width: calc(100% - 100px) !important; height: calc(100vh - 100px) !important;}.flatfile_iframe-wrapper.flatfile_displayAsModal { background: var(--ff-bg-fade) !important;}/* The close button in top right to close modal */.flatfile_displayAsModal .flatfile-close-button { display: block !important; margin: 20px !important;}/* The icon for the close button in top right to close modal */.flatfile_displayAsModal .flatfile-close-button svg { fill: var(--ff-secondary-color) !important;}/* The actual iframe that contains Flatfile */.flatfile_displayAsModal #flatfile_iframe { border-radius: var(--ff-border-radius);}/* Begin style overrides for when you cancel out of the Flatfile modal *//* The outer container of the modal that opens when you cancel out of Flatfile */.flatfile_outer-shell { background-color: var(--ff-bg-fade) !important; border-radius: var(--ff-border-radius) !important;}/* The inner container of the modal that opens when you cancel out of Flatfile *//* .flatfile_inner-shell {} *//* The white box inside the modal that opens when you cancel out of Flatfile */.flatfile_modal { border-radius: var(--ff-dialog-border-radius) !important;}/* The container for the buttons you see in the close modal *//* .flatfile_button-group {} *//* Style the buttons you see in the close modal *//* .flatfile_button {} *//* The "yes, cancel" button you see in the close modal */.flatfile_primary { border: 1px solid var(--ff-primary-color) !important; background-color: var(--ff-primary-color) !important; color: #fff;}/* The "no, stay" button you see in the close modal */.flatfile_secondary { color: var(--ff-secondary-color) !important;}/* The heading text you see in the close modal */.flatfile_modal-heading { color: var(--ff-text-color) !important;}/* The description text you see in the close modal */.flatfile_modal-text { color: var(--ff-secondary-color) !important;}/* End style overrides for when you cancel out of the Flatfile modal *//* End style overrides for when Flatfile is displayed as a modal *//* The container of the error component *//* .ff_error_container {}*//* The heading text you see in the error component *//* .ff_error_heading {}*//* The description text you see in the error component *//* .ff_error_text {}*/
Remember to add import './styles/styles.css' to your src/App.vue with the
other imports to apply the styles.
If you want to have a page to create new spaces, you can create a component to
do that fairly easily. The vue code should look fairly similar to Existing
Space, just with some different configuration settings. See below for the
necessary changes.