React Native and node js - fetching input values and using them in controllers - node.js

I have a react native component that has username and password text input fields. I want the values which are entered to be available in the node js controller to query my db. I am not able to use AsyncStorage as I cannot import AsyncStorage inside controller. How do I make this work? Please help.
My login screen:
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
Image,
TextInput,
Alert,
Navigator,
TouchableHighlight,
BackAndroid,
ScrollView,
AsyncStorage,
} from 'react-native';
var _navigator;
import Container from './Container';
import Button from './Button';
import Label from './Label';
import ImageContainer from './ImageContainer';
import RegisterView from './RegisterView';
export default class LoginView extends Component {
constructor(props) {
super(props);
this.state = {
userName: '',
userPass: '',
error:null,
};
}
_navigate(){
this.props.navigator.push({
name: 'RegisterView', // Matches route.name
})
}
_handleAdd = () => {
if((this.state.userName)!=="" && (this.state.userPass)!=="" ){
const data = {
username: this.state.userName,
password: this.state.userPass
}
// Serialize and post the data
const json = JSON.stringify(data)
fetch('http://10.0.2.2:3000/users/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: json
})
.then((response) => response.json())
.then((res) => {
if (res.error) {
alert(res.error)
} else {
this.props.navigator.push({
name: 'CheckList', // Matches route.name
})
}
})
.catch(() => {
alert('There was an error logging in.');
})
.done()
}
else{
alert('Cannot be empty!');
}
}
render() {
return (
<ScrollView style={styles.scroll}>
<ImageContainer>
<Image
style={{width:110,height: 110, justifyContent: 'center',
alignItems: 'center'}}
source={require('./Logo.png')}>
</Image>
</ImageContainer>
<Container>
<TextInput
placeholder="Username"
style={styles.textInput}
onChangeText={(text) => this.setState({userName:text})}
autoCapitalize="none"
autoCorrect={false}
onSubmitEditing={(event) => {
const{userName}=this.state.userName;
const{onSubmitEditing}=this.props;
if(!userName) return
onSubmitEditing(userName)
this.refs.SecondInput.focus();
}}
/>
<TextInput
placeholder="Password"
ref='SecondInput'
secureTextEntry={true}
onChangeText={(text) => this.setState({userPass:text})}
style={styles.textInputPass}
/>
</Container>
<Container>
<Button
label="Sign In"
styles={{button: styles.primaryButton, label:
styles.buttonWhiteText}}
onPress={() => this._handleAdd()}
/>
</Container>
<Container>
<TouchableHighlight onPress={ () => this._navigate()}
underlayColor= 'transparent'>
<Text style ={styles.buttonBlackText}>New? Register
</Text>
</TouchableHighlight>
</Container>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
scroll: {
backgroundColor: '#FFFFFF',
padding: 30,
flexDirection: 'column'
},
buttonWhiteText: {
fontSize: 20,
color: '#FFF'
},
buttonBlackText: {
fontSize: 20,
marginTop: 20,
textAlign:'center',
color: '#000000'
},
textInput: {
fontSize: 20,
backgroundColor: '#FFF',
marginBottom: 20,
marginTop: 20
},
textInputPass: {
fontSize: 20,
backgroundColor: '#FFF',
marginBottom: 20,
},
primaryButton: {
backgroundColor: '#34A853'
},
});
Controller to query the username from db. For now, it's hardcoded.
import Profile from '../models/profile';
import moment from 'moment';
export const index = (req, res, next) => {
Profile.find({'username': 'John'}).lean().exec((err, profiles) =>
res.json(
// Iterate through each movie
{ profiles: profiles.map(profile => ({
...profile,
}))}
));
};

Related

how to implement subscriptions to next.js app (apollo)?

i have a simple app, that fetch my database via graphql node.js server, and i want to implement subscriptions to it, backend is already written, but i don`t know what to do in frontend.
so i have a query and subscription, and i`m returning a map of data with messages and displaying it in my application, what i need to update, to make it work with subscriptions?
export const SUBSCRIPTION_MESSAGES = gql`
subscription {
messageCreated {
content
id
user
}
}
`;
export const GET_MESSAGES = gql`
query {
messages {
content
id
user
}
}
`;
<Messages user={state.user} />
--------------------
import { useQuery, useSubscription } from "#apollo/client";
import { useEffect } from "react";
import { GET_MESSAGES, SUBSCRIPTION_MESSAGES } from "../graphql/queries";
const Messages = ({ user }: any) => {
const { subscribeToMore, data } = useQuery(GET_MESSAGES);
console.log(subscribeToMore);
useEffect(() => {
subscribeToMore({
document: SUBSCRIPTION_MESSAGES,
updateQuery: (prev, { subscriptionData }: any) => {
if (!subscriptionData.data) return prev;
const newMessage = subscriptionData.data.messageCreated;
return Object.assign({}, prev, {
messages: [newMessage, ...prev.messages],
});
},
});
}, []);
if (!data) {
console.log("lol");
return null;
}
return (
<div>
{data.messages.map(({ id, user: messageUser, content }: any) => (
<div
key={id}
style={{
display: "flex",
justifyContent: user == messageUser ? "flex-end" : "flex-start",
paddingBottom: "1em",
}}
>
{user != messageUser && (
<div
style={{
height: 50,
width: 50,
marginRight: "0.5em",
border: "2px solid #e5e6ea",
borderRadius: 25,
textAlign: "center",
fontSize: "18pt",
paddingTop: 5,
}}
>
{messageUser.slice(0, 2).toUpperCase()}
</div>
)}
<div
style={{
background: user == messageUser ? "#58bf56" : "#e5e6ea",
color: user == messageUser ? "white" : "black",
paddingBottom: "1em",
borderRadius: "1em",
maxWidth: "60%",
padding: "10px",
}}
>
{content}
</div>
</div>
))}
</div>
);
};
export default Messages;

How to do payment with stripe in react native?

I'm creating a react native app and I need to do payment with stripe. Is there any way to do it with or without using any third-party libraries?
And I am able to create a Token with fetch method by accessing stripe API with the following code snippet.
Code Snippet
var cardDetails = {
'card[number]': this.state.cardData.values.number.replace(/ /g, ''),
'card[exp_month]': this.state.cardData.values.expiry.split('/')[0],
'card[exp_year]': this.state.cardData.values.expiry.split('/')[1],
'card[cvc]': this.state.cardData.values.cvc
};
var formBody = [];
for(var property in cardDetails){
var encodedKey = encodeURIComponent(property);
var encodedValue = encodeURIComponent(cardDetails[property]);
formBody.push(encodedKey + "=" + encodedValue);
}
formBody = formBody.join("&");
fetch('https://api.stripe.com/v1/tokens', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer '+ STRIPE_PUBLISHABLE_KEY
},
body: formBody
})
.then(responseJson => {
responseJson.json().then(solved=>{
cardToken =solved.id;
})
This is what I tried.
Here I used the react-native-credit-card-input library for taking credit card detail. I am new to react-native. And I don't know how to implement the code after this. I am able to get the token from the stripe by accessing 'https://api.stripe.com/v1/tokens' this URL with the above code snippet. But when I am going to create a charge with that token it gives me an error ('https://api.stripe.com/v1/charges'). What is the correct flow to create a payment in stripe with react-native?
import React, {Component} from 'react';
import {
View,
Text,
TouchableOpacity,
TextInput,
Platform,
StyleSheet,
StatusBar,
Alert,
Image,
ScrollView,
Animated,
Dimensions,
Modal,
} from 'react-native';
import FontAwesome from 'react-native-vector-icons/FontAwesome';
import Feather from 'react-native-vector-icons/Feather';
import {
CreditCardInput,
LiteCreditCardInput,
} from 'react-native-credit-card-input';
class stripePay extends Component {
constructor(props) {
super(props);
this.state = { cardData: { valid: false },tokenCRD:'' };
}
ContFunction = () => {
this.getCreditCardToken();
};
getCreditCardToken = () => {
var cardToken;
var STRIPE_PUBLISHABLE_KEY = 'pk_test_*****************';
var STRIPE_SECRET_KEY ='sk_test_*************************';
var cardDetails = {
'card[number]': this.state.cardData.values.number.replace(/ /g, ''),
'card[exp_month]': this.state.cardData.values.expiry.split('/')[0],
'card[exp_year]': this.state.cardData.values.expiry.split('/')[1],
'card[cvc]': this.state.cardData.values.cvc
};
var amount= 1*100;
var formBody = [];
for(var property in cardDetails){
var encodedKey = encodeURIComponent(property);
var encodedValue = encodeURIComponent(cardDetails[property]);
formBody.push(encodedKey + "=" + encodedValue);
}
formBody = formBody.join("&");
fetch('https://api.stripe.com/v1/tokens', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer '+ STRIPE_PUBLISHABLE_KEY
},
body: formBody
})
.then(responseJson => {
// console.log(responseJson);
responseJson.json().then(solved=>{
cardToken =solved.id;
// console.log(cardToken);
var paymentbody = {
amount: amount,
currency: 'usd',
description: 'Example charge',
source: cardToken,
//source: "tok_visa",
}
if(cardToken!=""){
//console.log(cardToken);
fetch('https://api.stripe.com/v1/charges', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer '+ STRIPE_SECRET_KEY
},
body:paymentbody
})
.then(response => response.json())
.then(responseJson => {
console.log(responseJson);
})
.catch(error => {
console.error(error);
});
}else{
console.log("Error")
}
});
})
.catch(error => {
console.error(error);
});
}
render() {
return (
<View style={styles.container}>
<StatusBar backgroundColor="#009387" barStyle="light-content" />
<View style={styles.header}>
<Text style={styles.text_header}>Testing Stripe Payment!</Text>
</View>
<View style={[styles.footer]}>
<View style={styles.cardContainer}>
<CreditCardInput
autoFocus
requiresName
requiresCVC
requiresPostalCode
labelStyle={styles.label}
inputStyle={styles.input}
validColor={'black'}
invalidColor={'red'}
placeholderColor={'darkgray'}
onFocus={this._onFocus}
// onChange={this._onChange}
onChange={(cardData) => this.setState({ cardData })}
/>
</View>
<View style={styles.button1}>
<TouchableOpacity
style={styles.btnCon}
onPress={() => this.ContFunction()}>
<View
style={styles.btnCon}>
<Text style={[styles.textCon]}>Pay Now</Text>
</View>
</TouchableOpacity>
</View>
</View>
</View>
);
}
}
export default stripePay;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#009387',
},
header: {
flex: 1,
justifyContent: 'flex-end',
paddingHorizontal: 20,
paddingBottom: 30,
},
text_header: {
color: '#fff',
fontWeight: 'bold',
fontSize: 30,
},
footer: {
flex: 3,
backgroundColor: '#fff',
borderTopLeftRadius: 30,
borderTopRightRadius: 30,
paddingHorizontal: 20,
paddingVertical: 30,
},
cardContainer: {
backgroundColor: '#fff',
marginTop: 60,
},
label: {
color: 'black',
fontSize: 12,
},
input: {
fontSize: 16,
color: 'black',
},
button1: {
alignItems: 'center',
marginTop: 40,
// width:'80%',
},
btnCon: {
width: '80%',
height: 50,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 10,
backgroundColor: '#00b530',
},
textCon: {
fontSize: 18,
fontWeight: 'bold',
color: 'white',
},
});
I got the following error.
All stripe payments can be handled through api using fetch, so there is no need in using 3rd party libraries.
You need to create a customer, add the card to the customer, then create the payment, create one-time-use card token and confirm the payment with the card token.
Payment verification is handled through the link in response, which is supposed to be opened in browser, where the user can confirm the payment manually. In this case, use the PaymentIntents api.

send image taken from ReactNativeCamera to Nodejs backend that has multer?

I have a frontend with React native and I implemented the following:
import React, {Component} from 'react';
import Config from 'react-native-config';
import {
Button,
Alert,
Image,
Text,
View,
StyleSheet,
TouchableOpacity,
Platform,
CameraRoll,
} from 'react-native';
import {Container, Content, Icon} from 'native-base';
import {RNCamera} from 'react-native-camera';
import {SubHeader} from '../../components/headers';
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
backgroundColor: 'black',
},
preview: {
flex: 1,
justifyContent: 'flex-end',
alignItems: 'center',
},
capture: {
flex: 0,
backgroundColor: '#fff',
borderRadius: 5,
padding: 15,
paddingHorizontal: 20,
alignSelf: 'center',
margin: 20,
},
});
export default class MainScreen extends Component {
constructor(props) {
super(props);
this.state = {
imageUri: null,
};
}
test = async () => {
if (this.camera) {
const data = await this.camera.takePictureAsync({
quality: 0.5,
base64: true,
});
const photo = {
name: data.fileName,
type: data.type,
uri:
Platform.OS === 'android'
? data.uri
: data.uri.replace('file://', ''),
};
this.setState({imageUri: data.uri}); //preview
const fdata = new FormData();
fdata.append('file', photo);
try {
let response = await fetch(`${Config.BASE_URL}/scan`, {
method: 'POST',
body: JSON.stringify(fdata),
//body: fdata //this launches a connection error
});
const res = await response.json();
return res;
} catch (error) {
console.error(error);
}
} else {
Alert.alert('debug', 'There is no camera');
}
};
render() {
const {navigate} = this.props.navigation;
return (
<View style={styles.container}>
<RNCamera
ref={ref => {
this.camera = ref;
}}
style={styles.preview}
type={RNCamera.Constants.Type.back}
flashMode={RNCamera.Constants.FlashMode.off}
captureAudio={false}
androidCameraPermissionOptions={{
title: 'Permission to use camera',
message: 'We need your permission to use your camera',
buttonPositive: 'Ok',
buttonNegative: 'Cancel',
}}
onGoogleVisionBarcodesDetected={({barcodes}) => {
console.log(barcodes);
}}
/>
<View>
<TouchableOpacity
onPress={this.test.bind(this)}
style={styles.capture}>
<Icon type="FontAwesome" ios="camera" android="camera" />
</TouchableOpacity>
</View>
</View>
//preview
<Image
style={{width: 66, height: 58}}
source={{
uri: this.state.imageUri,
}}
/>
</View>
);
}
}
On the backend with nodeJs express and multer:
app.post('/scan', uploader.upload.single('file'), ScanController.scan);
Multer is well implemented because it is working with postman and with frontend web application fucntionalities.
the image is shown on the android device, but I am getting always an undefined object on the backend, i do not know how to send this because is on base 64, How can I send it or receive correctly?
There is an open issue See here on react native about this, but I found a nice solution using rn-fetch-blob
as you may see in the documentation, you can implement the following:
First, delete the FormData instance, then change the fetch for RNFetchBlob.fetch
Code as follows
upload = async () => {
let ret = await RNFetchBlob.fetch(
'POST',
`${Config.BASE_URL}/scan`,
{
'Content-Type': 'multipart/form-data',
},
[
{
name: 'file',
filename: Date.now() + '.png',
type: 'image/png',
data: RNFetchBlob.wrap(data.uri),
},
],
);
return ret;
};
As you see, there is an array, where you can add the objects.
Multer implementation and RNCamera stays the same.
Hope this could be useful to someone!

Socket.emit not sending or socket.on not receiving?

I am building a mobile Chat app using Node.js, MySql, Sequelize, Socket, React Native and Redux.
I am trying to socket.emit the credential of the user to:
1) find or create the user and;
2) move to the next screen via the reducer.
I believe that either:
socket.emit('newUser', credentials) in ./store/index.js
doesn't work, and / or both
socket.on('newUser', credentials => {...} in ./index.js
socket.on('newUser', user => {...} in ./store/index.js
do not work, because nothing is added to database an I do not move to next screen. Simply, nothing happen when I enter some credentials and hit the button.
I am fairly new to development and tried to understand where is the problem by using multiple console.log (removed from the code below), as I don't know how else to test it.
I have also checked all other threads on socket.emit and on how to test if socket is working for the past few days, but I am still stuck.
Below is my code.
1) why socket.emit doesn't send (or socket.on doesn't listen)?
2) How can I test if socket is working (both on client and server side).
Thank you!!
./index.js
const server = require('http');
server.createServer(function (req, res) {}).listen(3000);
const io = require('socket.io')(server);
const { User, Conversation, Message } = require('./db').models;
const mobileSockets = {};
io.on('connection', socket => {
socket.on('newUser', credentials => {
const { name, password } = credentials;
Promise.all([
User.findOrCreate({
where: {
name,
password
}
}),
User.findAll()
])
.then(([user, users]) => {
mobileSockets[user[0].id] = socket.id;
socket.emit('userCreated', { user: user[0], users });
socket.broadcast.emit('newUser', user[0]);
});
});
});
./App.js
import React from 'react';
import { Provider } from 'react-redux';
import store from './store';
import { createStackNavigator } from 'react-navigation';
export default class App extends React.Component {
render() {
return (
<Provider store={ store }>
<RootStack />
</Provider>
);
}
}
import LoginScreen from './components/Login';
import Users from './components/Users';
import Chat from './components/Chat';
const RootStack = createStackNavigator({
Login: LoginScreen,
Users: Users,
Chat: Chat,
}, {
initialRouteName: 'Login',
navigationOptions: {
headerTitle: 'login to Chat!'
}
});
./components/Login.js
import React from 'react';
import { View, Text, TextInput, StyleSheet, TouchableOpacity } from 'react-native';
import { login } from '../store';
export default class LoginScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
password: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(type, value) {
this.setState({ [type]: value });
}
handleSubmit() {
login(this.state, this.props.navigation);
}
render() {
return (
<View style={ styles.container }>
<Text style={ styles.text }>Enter your name and password:</Text>
<TextInput
onChangeText={ value => this.handleChange('name', value) }
returnKeyType='next'
autoCapitalize='none'
autoCorrect={ false }
onSubmitEditing={ () => this.passwordInput.focus() }
style={ styles.input }
/>
<TextInput
onChangeText={ value => this.handleChange('password', value)}
secureTextEntry
returnKeyType='go'
autoCapitalize='none'
style={ styles.input }
ref={ input => this.passwordInput = input }
/>
<TouchableOpacity
onPress={ this.handleSubmit }
style={ styles.button }
>
<Text style={ styles.buttonText }>Login</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'powderblue',
height: '100%',
width: '100%'
},
text: {
fontSize: 20,
fontWeight: 'bold'
},
input: {
height: 40,
width: '90%',
borderWidth: 0.5,
borderColor: 'black',
backgroundColor: '#fff',
color: '#000',
textAlign: 'center',
marginTop: 10
},
button: {
width: '75%',
backgroundColor: 'blue',
borderRadius: 50,
alignItems: 'center',
justifyContent: 'center',
marginTop: 20,
paddingVertical: 15
},
buttonText: {
color: '#fff',
textAlign: 'center',
fontSize: 15,
fontWeight: 'bold',
}
});
./store/index.js
import { createStore, combineReducers } from 'redux';
import users, { gotUsers, gotNewUser } from './users';
import messages, { gotMessages, gotMessage } from './messages';
import user, { gotUser } from './user';
let navigate;
const reducers = combineReducers({ users, messages, user });
const store = createStore(reducers);
export default store;
export * from './users';
export * from './messages';
import socket from './socket';
});
socket.on('userCreated', response => {
const { user, users } = response;
store.dispatch(gotUser(user));
store.dispatch(gotUsers(users));
navigate('Users');
});
socket.on('newUser', user => {
console.log('store/index.js has received a "newUser"');
store.dispatch(gotNewUser(user));
});
export const login = (credentials, navigation) => {
socket.emit('newUser', credentials);
navigation = navigation.navigate;
};
./store/socket.js
import io from 'socket.io-client';
const socket = io('http://localhost:3000');
socket.connect();
export default socket;
It's not a real solution to the problem, but I've managed to make it work by using express.
./index.js
// replaced the connection part of the code with this
const app = require('express')();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
app.get('/', function (req, res) {
res.send('Hello World');
});
server.listen(3000);
// the rest of the code goes here

React Native - can't import mongoose on the front end(but it works from the backend)

I cant import mongoose on the frontend but it worked on the backend.
I have a separate directory for the backend. I have some code to add a couple users to my database. Here it is.....
import mongoose from 'mongoose';
import User from './models/user';
const users = [
{
id: 1,
username: 'Matthew',
password: 'Smith',
},
{
id: 2,
username: 'Deborah',
password: 'Smith',
},
];
// Connect to MongoDB
mongoose.connect('mongodb://localhost/users');
// Go through each movie
users.map(data => {
// Initialize a model with movie data
const user = new User(data);
// and save it into the database
user.save();
});
The above code works.
Now I want my login screen to accept the username and password and send it to the database. I'm stuck because I cant even import mongoose(you'll see below that I commented it out).
The error I get is:
'TransformError: [directory path of my project]/node_modules/mongoose/lib/drivers/index.js: require() must have a single string literal argument: [directory path of my project]/mongoose/lib/drivers/index.js:7'
The login screen code:
import React from 'react';
import { StyleSheet, Text, View,Navigator,TextInput, KeyboardAvoidingView,TouchableOpacity,
AsyncStorage,
} from 'react-native';
//import mongoose from 'mongoose';
import {
StackNavigator,
} from 'react-navigation';
export default class Login extends React.Component {
//check to see if user has logged in already
componentDidMount(){
this._loadInitialState().done();
}
//get info from async storage
_loadInitialState = async () => {
var value = await AsyncStorage.getItem('user');
if(value != null){ //if the user is already logged in
this.props.navigation.navigate('Profile'); //**profile page that we will create later
}
}
constructor(props){
super(props);
this.state = {
username: '',
password: '',
}
}
render() {
return (
//<View style={styles.container}>
<KeyboardAvoidingView behavior = 'padding' style = {styles.wrapper}>
<View style = {styles.container}>
<Text style={styles.header}> - LOGIN - </Text>
<TextInput
style={styles.textInput} placeholder='Username'
onChangeText={(username) => this.setState({username})}
/>
<TextInput
style={styles.textInput} placeholder='Password'
onChangeText={(password) => this.setState({password})}
/>
</View>
<TouchableOpacity
style={styles.btn}
onPress = {this.login}>
<Text>Log in</Text>
</TouchableOpacity>
</KeyboardAvoidingView>
// </View>
);
}
login = () => {
alert('test');
//send to server
fetch('http://1.1.1.1/3000/users', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: this.state.username,
password: this.state.password,
})
})
//handle response
.then((response) => response.json())
.then((res) => {
//if user and pass exists, then log them in
// res: result
if(res.sucess === true){
AysncStorage.setItem('user',res.user);
this.props.navigation.navigate('Profile'); //navigate user to profile page
}
//else, tell the user they dont exist in the database
else{
alert(res.message);
}
})
.done();
}
}
const styles = StyleSheet.create({
wrapper: {
flex: 1,
},
container: {
flex: 1,
backgroundColor: '#2896d3',
alignItems: 'center',
justifyContent: 'center',
paddingLeft: 40,
paddingRight: 40,
},
header: {
fontSize: 24,
marginBottom: 60,
color: '#fff',
justifyContent: 'center',
paddingLeft: 40,
paddingRight: 40,
},
textInput: {
alignSelf: 'stretch',
paddingLeft: 16,
marginBottom: 20,
backgroundColor: '#fff',
},
btn: {
alignSelf: 'stretch',
padding: 20,
marginBottom: 20,
backgroundColor: '#01c853',
alignItems: 'center',
},
});
Why can't I import it?
Mongoose's browser library doesn't support mongoose.connect(). It only supports schemas and document validation, it can't actually connect to MongoDB or save documents currently.
Just today I found out about Mongoose's browser module while trying to solve similar problems to you. It's a very limited version of Mongoose but will let you do validation against schemas, for example, on the front-end. Sorry I only found this 6 months after your post.

Resources